P1052 [NOIP2005 提高组] 过河

P1052 [NOIP2005 提高组\] 过河 - 洛谷 \| 计算机科学教育新生态 (luogu.com.cn)](https://www.luogu.com.cn/problem/P1052) 问题描述:给定长度L,和一次可以跳动的长度 s 到 t,给定m个石头的位置,求最少经过多少个石头可以超过L。 思路:如果L很小的话,就是简单dp。 i f i 有石头 F ( i ) = m i n ( F ( i ) , F ( i − j ) + 1 ) j ∈ \[ s , t \] e l s e F ( i ) = m i n ( F ( i ) , F ( i − j ) ) j ∈ \[ s , t \] if \\quad i有石头 \\quad F(i) = min(F(i), F(i - j) + 1) \\quad j \\in \[s,t\] \\\\ else \\quad F(i) = min(F(i), F(i-j)) \\quad j \\in \[s,t\] ifi有石头F(i)=min(F(i),F(i−j)+1)j∈\[s,t\]elseF(i)=min(F(i),F(i−j))j∈\[s,t

但是发现,L特别大,但是石头个数却特别小,同时也发现s和t也很小,就算m * t * s最大也才1000。如果将石头距离进行缩小就可以过。

对于 两个石头距离大于s * t的来说,对于区间[s * t, 两个石头之间的距离]都是可以经过跳[s, t]这些个数给到达的。因此,可以将两个石头距离大于s * t的缩小为s * t,这样就可以用上面的状态转移方程。

缩点

cpp 复制代码
    int st = s * t;
    rep(i,1,m) {
        int dist = a[i] - a[i-1];
        if(dist >= st) dist = st;
        ph[i] = ph[i-1] + dist;
        // 将石头所在的那个点进行赋值为 true
        vis[ph[i]] = 1;
    }

状态转移方程

cpp 复制代码
    int len = ph[m] + st; 
    memset(f, 0x3f, sizeof(f));
    f[0] = 0;
    rep(i,1,len) {
        rep(j,s,t) {
            if(i - j >= 0) {
                if(vis[i]) f[i] = min(f[i-j] + 1, f[i]);
                else f[i] = min(f[i-j], f[i]);
            }
        }
    }

求答案

cpp 复制代码
    int ans = INF;
    rep(i,ph[m],len) {
        ans = min(ans, f[i]);
    }

s == t进行特判

cpp 复制代码
    if(s == t) { // 特判 s == t
        int cnt = 0;
        rep(i,1,m) if(a[i] % s == 0) cnt++;
        cout<<cnt;
        return ;
    }

AC代码

cpp 复制代码
const int N = 2e5 + 21;
int a[N], f[N],ph[N];
bool vis[N];
void solve() {
    int L,s,t,m; cin>>L>>s>>t>>m;
    rep(i,1,m) cin>>a[i];
    // 需要进行排序,石头位置初始是无序的
    sort(a+1, a+m+1);
    if(s == t) { // 特判 s == t
        int cnt = 0;
        rep(i,1,m) if(a[i] % s == 0) cnt++;
        cout<<cnt;
        return ;
    }
    // 如果 两个石头之间的距离大于等于 s * t,进行缩点
    /**
     * 因为,假设 两个石头距离为 len
     * 如果 len > s * t,则在 [s*t, len] 这个区间内的每一个点都可以访问到
    */
    int st = s * t;
    rep(i,1,m) {
        int dist = a[i] - a[i-1];
        if(dist >= st) dist = st;
        ph[i] = ph[i-1] + dist;
        // 将石头所在的那个点进行赋值为 true
        vis[ph[i]] = 1;
    }
    // 因为是大于L就行,因此可能有超过L,但是是最小次数的情况
    int len = ph[m] + st; 
    memset(f, 0x3f, sizeof(f));
    f[0] = 0;
    rep(i,1,len) {
        rep(j,s,t) {
            if(i - j >= 0) {
                if(vis[i]) f[i] = min(f[i-j] + 1, f[i]);
                else f[i] = min(f[i-j], f[i]);
            }
        }
    }
    int ans = INF;
    rep(i,ph[m],len) {
        ans = min(ans, f[i]);
    }
    cout<<ans;
}
相关推荐
梦梦代码精27 分钟前
BuildingAI 上部署自定义工作流智能体:5 个实用技巧
大数据·人工智能·算法·开源软件
Zephyr_01 小时前
Leedcode算法题
java·算法
流年如夢2 小时前
栈和列队(LeetCode)
数据结构·算法·leetcode·链表·职场和发展
Hello.Reader3 小时前
算法基础(十)——分治思想把大问题拆成小问题
java·开发语言·算法
绛橘色的日落(。・∀・)ノ4 小时前
机器学习之评估与偏差方差分析
算法
消失的旧时光-19434 小时前
C语言对象模型系列(四)《Linux 内核里的 container_of 到底是什么黑魔法?》—— 一篇讲透 Linux 内核的“对象模型”核心技巧
linux·c语言·算法
AI_Ming4 小时前
从0开始学AI:层归一化,原来是这回事!
算法·ai编程
WL_Aurora4 小时前
备战蓝桥杯国赛【Day 8】
算法·蓝桥杯
智者知已应修善业5 小时前
【51单片机模拟生日蜡烛】2023-10-10
c++·经验分享·笔记·算法·51单片机