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;
}
相关推荐
北极有牛7 小时前
cpp学习笔记--常量指针
java·开发语言·算法
GPU实战笔记7 小时前
云端 GPU 训练的账户余额提醒:它能说明什么,不能说明什么?
深度学习·算法·成本管理·gpu云计算·云端训练
浮链序7 小时前
怎么证明"你这个模型是偷我的"——把蒸馏变成取证工具
算法·安全·llm
小O的算法实验室7 小时前
IEEE TMC,面向异构物联网的多目标量子行为拓扑优化算法
算法
liliangcsdn8 小时前
派息率指标的应用探索和分析
大数据·算法
垆边人似月.8 小时前
华为机试题 :两数之和
c++·算法·华为
默默开发8 小时前
物流行业可用算法方向
算法
垆边人似月.8 小时前
华为机试题 :有效的括号
c++·算法·华为
A加ITIL教练长河achotsao9 小时前
ITIL Master 战略领导者课后系列分享:AI编程正在重新划分研发与运维的边界
算法
hahaha60169 小时前
FPGA+ARM实现AWB的全流程
人工智能·嵌入式硬件·算法·计算机视觉·fpga开发