【NOIP2005提高组T2】过河-DP+路径压缩
测试地址:过河
做法:30分的做法很容易想,设f[i]为走到i时所踩的最少的石子数,则状态转移方程为:f[i]=f[i-x](i处无石子)或f[i-x]+1(i处有石子),其中S≤x≤T。但是L高达10亿,我们就要想办法优化。
首先是空间上的,由于我们在状态转移方程中只用到了f[i-S]到f[i-T],所以使用滚动数组即可。
然后是时间上的,我们知道,虽然L很大,但是石子的数目M很小,这样就会产生一种情况:如果两个石子中间有很大一块空白,那么就会浪费很多时间,因为在这期间最少石子数都不会变。在上述的滚动数组定义下,就表现为:对于所有的下标,f[]的值相同。一旦出现这种情况,只要下一步没有石子,f[]的值都不变,所以我们可以直接跳到下一个石子所在的地方进行计算。注意,上述性质在S=T的时候不成立,特判一下即可。
另外还需要注意,输入数据中给的石子位置是乱序,需要先排序。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define inf 999999999
using namespace std;
int l,s,t,m,f[15]={0};
int pos[110]={0};
int main()
{
scanf("%d",&l);
scanf("%d%d%d",&s,&t,&m);
for(int i=1;i<=m;i++)
scanf("%d",&pos[i]);
pos[0]=0;pos[m+1]=0;
if (s!=t)
{
sort(pos+1,pos+m+1,less<int>());
int i=s,p=1;
int last=101,cnt=0;
f[0]=0;
for(int j=1;j<t;j++) f[j]=101;
while(i<=l+t)
{
if (i==pos[p]) f[i%t]++;
for(int j=t;j>=s;j--) f[i%t]=min(f[i%t],f[(i-j+t)%t]+(i==pos[p]));
if (i>=pos[p]&&p<=m) p++;
if (f[i%t]==last) cnt++;
else
{
last=f[i%t];
cnt=1;
}
i++;
if (cnt>t&&i<l)
{
if (p<=m) i=pos[p];
else i=l;
cnt=0;
}
}
int ans=inf;
for(int j=0;j<t;j++)
ans=min(ans,f[j]);
printf("%d",ans);
}
else
{
int ans=0;
for(int i=1;i<=m;i++)
if (pos[i]%s==0) ans++;
printf("%d",ans);
}
return 0;
}