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,telseF(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;
}
相关推荐
wabs6664 小时前
关于贪心算法的思考
算法·贪心算法
社交怪人4 小时前
【判断大小】信息学奥赛一本通C语言解法(题号1043)
算法
Snasph5 小时前
GNU Make 用户手册(中文版)
服务器·算法·gnu
江澎涌5 小时前
拆解与 AI 的一次对话
人工智能·算法·程序员
sheeta19985 小时前
LeetCode 每日一题笔记 日期:2026.06.02 题目:3635. 最早完成陆地和水上游乐设施的时间 II
笔记·算法·leetcode
Lsk_Smion6 小时前
力扣实训 _ [102].层序遍历--前序--后续_递归与非递归的实现
数据结构·算法·leetcode
Lsk_Smion6 小时前
力扣实训 _ [25].K个一组链表
数据结构·链表
小欣加油7 小时前
leetcode3751 范围内总波动值I
java·数据结构·c++·算法·leetcode
Halo_tjn8 小时前
反射与设计模式1
java·开发语言·算法
V搜xhliang02469 小时前
临床科研新范式:从选题到投稿,AI智能体如何接管全流程?
运维·数据结构·人工智能·算法·microsoft·数据挖掘·自动化