【洛谷P1052】过河 离散化+dp

题目大意:给定一个长度为 N 的序列,有 M 个点对答案的贡献为 1,其余为 0,现从起点出发,每次只能走 [s,t] 个单位,求从起点走到终点时答案贡献最小是多少。

题解:由于 N 很大,无法直接记录状态。观察发现 M 很小,且 [s,t] 也很小,因此,考虑到只有在答案贡献为 1 的点的附近 dp 值才可能会发生变化,其余位置会导致大量的解的重复而浪费时间和空间。基于以上想法,考虑缩点,即:对于两个石子之间的距离来说,是否存在一个点 s0 使得当 s>s0 时,无论从前一个石子之前的任何位置,通过任何步数的组合,均可以到达 s ,则从 s0 到下一个石子的位置之前的 0 贡献部分可以被缩掉。这样,根据裴蜀定理可得,对于 \(len>t(t-1)\) 时,即可满足上述条件。

代码如下

#include 
using namespace std;
const int maxn=1e4+10;

int n,s,t,m,l,pos[101];
int dp[maxn];
bool is[maxn];

void read_and_parse(){
    scanf("%d%d%d%d",&n,&s,&t,&m);
    for(int i=1;i<=m;i++)scanf("%d",&pos[i]);
}

void solve(){
    if(s==t){
        int ans=0;
        for(int i=1;i<=m;i++)if(pos[i]%s==0)++ans;
        return (void)printf("%d\n",ans);
    }
    sort(pos+1,pos+m+1);
    for(int i=1;i<=m;i++)l+=min(pos[i]-pos[i-1],100),is[l]=1;
    l+=min(n-pos[m],100);
    memset(dp,0x3f,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=l+9;i++)
        for(int j=s;j<=t;j++)if(i>=j)
            dp[i]=min(dp[i],dp[i-j]+is[i]);
    int ans=1e9;
    for(int i=l;i<=l+9;i++)ans=min(ans,dp[i]);
    printf("%d\n",ans);
}

int main(){
    read_and_parse();
    solve();
    return 0;   
} 

转载于:https://www.cnblogs.com/wzj-xhjbk/p/10596676.html

你可能感兴趣的:(【洛谷P1052】过河 离散化+dp)