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;
}
相关推荐
不爱吃炸鸡柳15 分钟前
算法复杂度从入门到精通:时间与空间复杂度全解析
开发语言·c++·算法
拳里剑气21 分钟前
C++算法:二分查找
c++·算法·二分查找·学习方法
黎阳之光33 分钟前
去标签化定位时代:黎阳之光自研技术,可见即可定位,无感亦能解算
大数据·人工智能·算法·安全·数字孪生
故事和你9141 分钟前
洛谷-算法1-7-搜索2
数据结构·c++·算法·leetcode·深度优先·动态规划·图论
炽烈小老头1 小时前
【每天学习一点算法 2026/094/14】分数到小数
学习·算法
_深海凉_1 小时前
LeetCode热题100-和为 K 的子数组
数据结构·算法
深紫色的三北六号1 小时前
仿大疆司空2面状航线生成——凸多边形区域航线生成算法详解
java·算法·无人机·大疆·航线规划
YuanDaima20482 小时前
双指针基础原理与题目说明
数据结构·人工智能·python·算法·leetcode·手撕代码
别或许2 小时前
5、高数----一元函数微分学的应用(一)几何应用
算法