#include<iostream>
#include<algorithm>
using namespace std;
int a[105],d[105],stone[350000];
int f[350000]; //压缩路径后f[]就不必开10^9那么大了
int main()
{
int l,s,t,m;
cin>>l>>s>>t>>m;
for (int i=1;i<=m;i++)
cin>>a[i];
sort(a+1,a+m+1); //输入石子坐标可能无序
for (int i=1;i<=m;i++)
d[i]=(a[i]-a[i-1])%2520; //要对1~10的最小公倍数取余,压缩路径的核心
for (int i=1;i<=m;i++)
{
a[i]=a[i-1]+d[i];
stone[a[i]]=1; //此处有石子,标记
}
l=a[m]; //压缩路径后的总长度
for (int i=0;i<=l+t;i++) f[i]=m; //f[i]表示到位置i最少能踩到的石子数
f[0]=0;
//以上是初始化,接下来是动归
for (int i=1;i<=l+t;i++)
for (int j=s;j<=t;j++)
{
if (i-j>=0)
f[i]=min(f[i],f[i-j]); //状态转移方程
f[i]+=stone[i];
}
int ans=m;
for (int i=l;i<l+t;i++) ans=min(ans,f[i]);
cout<<ans<<endl;
return 0;
}
这篇是个人认为比较清晰的题解,可惜被这组数据HACK了
10000
8 9 2
2528 5049
应输出0,这份代码输出1
锅出在哪?
仔细观察2520缩的结果可以发现
10000
8 9 2
2528 5049
2528->8
5049->9
即缩完变成 8 9
两个原本距离很远的点被拉进了
原来可以先跳到2520
再2520+9从而跳过2528
然后如果直接2520+2529=5049显然不优
我们可以后退一个8,再跳一个9即可避开5049
即5041+9=5050
从而可以得到在距离被拉进的两个点之间的距离+t即可处理这样的错误
注意不能无脑加t拉开距离,不然会出现一些原本距离很近由于加t而产生的一些不合法的更优解
附上AC代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2.6e5;
int a[110],dis[110],m,L,s,t;
int dp[maxn],is[maxn];
int main(){
//freopen("P1052_1.in","r",stdin);
//freopen("my.txt","w",stdout);
scanf("%d%d%d%d",&L,&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++){
if(a[i]-a[i-1]>=2520) dis[i]=(a[i]-a[i-1])%2520+t;
else dis[i]=(a[i]-a[i-1])%2520;
}
for(int i=1;i<=m;i++){
a[i]=a[i-1]+dis[i];
is[a[i]]=1;
}
//for(int i=1;i<=m;i++) printf("a[%d]=%d\n",i,a[i]);
L=a[m];
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=L+t;i++)
for(int j=s;j<=t;j++){
if(i-j>=0) dp[i]=min(dp[i],dp[i-j]+is[i]);
}
int ans=dp[L];
for(int i=L;i<L+t;i++)
ans=min(ans,dp[i]);
printf("%d",ans);
return 0;
}
再说说关于数组要开多大,取一个极限
即每个石头两两相差2519且t=10
则数组大小为(2519+10)*100
开2.6e5即可
当然空间肯定够用,可以开大一点
如果我说的有错误的地方请各位神犇指正