题意

  • 给定桥的长度L,每一步可以走S~T中任意距离,桥上共有M颗石子,求走过桥最少踩多少石子
  • 0<= L <=1e9, 1<=S<=T<=10, 1<=M<=100

思路

  • 一个类似于走楼梯的动态规划,很显然的可以得到转移方程:其中,S<=j<=T,0<=i<=L
  • 但是,由于L的范围很大,不可能开下这么大的dp数组,所以需要对长度进行压缩
  • 参考NOIP2017|小凯的疑惑,数字a,b不能表示的最大数是
  • 因为最大步长为10,所以所有超过71的数都可以等价的压缩成72
  • 完成路径压缩后即可正常处理,对于L由于不要求恰好过桥,所以i的范围可以变为1<=i<=压缩后的最后一个石头+1

AC代码

#include<bits/stdc++.h>
using namespace std;

int a[200],b[200],vis[20202];
int dp[20202];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int L,S,T,M;
    memset(dp,0x3f3f3f3f,sizeof(dp));

    cin >> L >> S >> T >> M ;
    if(S==T){
        int ans=0;
        for(int i=1;i<=M;i++){
            cin >> a[i] ;
            if(a[i]%S==0) ans++;
        }
        cout << ans << '\n';
        return 0;
    }
    for(int i=1;i<=M;i++){
        cin >> a[i] ;
    }
    sort(a+1,a+1+M);

    //路径压缩
    for(int i=1;i<=M;i++){
        if(a[i]-a[i-1]>72){
            b[i]=b[i-1]+72;
        }else{
            b[i]=b[i-1]+(a[i]-a[i-1]);
        }
    }
    
    for(int i=1;i<=M;i++) vis[b[i]]=1;
    // for(int i=1;i<=M;i++) cout << b[i] << ' ' ;
    
    b[M+1]=b[M]+1;
    dp[0]=0;
    // cout << b[M+1] << '\n';
    for(int i=1;i<=b[M+1];i++){
        for(int j=S;j<=T;j++){
            if(i<j) continue;
            // cout << i << ' ' << j << '\n';
            dp[i]=min(dp[i],dp[i-j]+vis[i]);
        }
    }
    cout << dp[b[M+1]] << '\n' ;
    return 0;
    
}