设当前是从 a 点跳到 b 点, b 点可能有石头(说明 a 点和 b 点之间,一定没有石头,怎么跳都不影响答案);
按照下面的图分析:可以分情况讨论: 1 如果 a < b< a+2520,则正常操作; 2 如果 a + 2520 < b,则让 a 先跳到 a+2520 的点上,再重复 1 的操作; 3 依次类推,如果 a + x * 2520 < b,可以看作 a 可以直接飞过中间的x个区间,到离 b 最近的一个位置,在按照1的操作执行;
根据以上的操作,可以把任意两个点之间的距离d,压缩到 2520 以内,因此 f 数组的空间就可以从原来 109 缩小到 50W以内,然后就可以正常跑DP了。
还有一个细节,石头的位置需要先排序(题目没有说按顺序给);
100分参考代码
//luogu1052:过河
//线性DP+状态压缩
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t,ans=999999999;
int a[210];
int f[1000005];
int b[1000005];
int d[210];
int main()
{
scanf("%d",&n);//桥的长度
scanf("%d %d %d",&s,&t,&m);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
sort(a+1,a+1+m);
//距离压缩
for(int i=1;i<=m;i++) d[i]=(a[i]-a[i-1])%2520;//计算出石子之间的距离(压缩后)
for(int i=1;i<=m;i++)
{
a[i]=a[i-1]+d[i];//更新石子的位置
b[a[i]]=1;
}
n=a[m];//因为距离压缩了,对岸也压缩了
//正常跑DP
memset(f,127,sizeof(f));
f[0]=0;
for(int i=1;i<=n+t;i++)
{
for(int j=s;j<=t;j++)
{
if(i-j>=0) f[i]=min(f[i],f[i-j])+b[i];
}
}
for(int i=n;i<n+t;i++) ans=min(ans,f[i]);
printf("%d",ans);
return 0;
}