這是我寫的在Vijos上的第一題。這道題在我剛學完DP的時候,就做過。當時年少輕狂,沒有看數據的范圍,直接暴力DP,結果TLE。。。。後來就沒有再碰過。知道最近覺得快要省賽了,有必要把原來沒有做出來的題做一做,於是有了這篇博客。
從那時學完的最簡單的動規後,又學了一個名叫狀壓DP的算法,狀壓即狀態壓縮,把沒有用的狀態全部排除掉。BZOJ上就有一道狀壓DP的題(互不侵犯king)傳送門!!而過河這道題就是一道狀壓DP。根據我現在的習慣,做題先看數據范圍,然後判定算法。(1 <= L <= 10^9)這麼大的數,根據經驗應該用log級的算法,但是再看題目顯然用不到任何log級的算法。而應該是DP。那麼既然是DP又有這麼大的數據范圍,所以應該是狀壓DP。這樣算法就判斷出來了。那麼該怎麼壓縮呢?這裡要用到一個定理
Px+(P+1)y=Q
x,y是未知數,P是跳躍的距離,P+1也是跳躍的距離,對於任意Q,Q>=P*(P-1), x,y一定存在正整數解,換句話說當兩個石子之間的距離大於等於T*(T-1)時,中間有相當大的距離是每個點都可以跳到的,因為沒有石子,所以對答案沒有貢獻,可以取模(%90),這樣就很好辦了。。。於是我們就成功地把狀態給壓縮了。
注意:石子並沒有按順序給出要排序。
代碼:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define inf 1<<30
using namespace std;
int l,s,t,m,f[900000],pos[200],a[900000];
void slove(){
int ans=0;
for(int i=1;i<=m;i++)
if(pos[i]%s==0) ans++;
printf("%d",ans);
}
int main()
{
scanf("%d%d%d%d",&l,&s,&t,&m);
for(int i=1;i<=m;i++)
scanf("%d",&pos[i]);
sort(pos+1,pos+m+1);
if(s==t){
slove();
return 0;
}
for(int i=1;i<=m;i++){
int temp=pos[i]-pos[i-1];
pos[i]=pos[i-1]+temp%90;
}
l=pos[m]+(l-pos[m])%90;
for(int i=1;i<=m;i++)
a[pos[i]]=1;
for(int i=1;i<=l;i++)
f[i]=inf;
f[0]=0;
for(int i=1;i<=l;i++)
for(int j=s;j<=t;j++)
if(i-j>=0)
f[i]=min(f[i-j]+a[i],f[i]);
printf("%d",f[l]);
}