【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;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793864.html