F. Mattress Run 題解
阿新 • • 發佈:2021-10-03
F. Mattress Run
挺好的一道題,對於DP的本質的理解有很大的幫助。
首先要想到的就是將這個拆成兩個題,一個dp光求獲得足夠的夜晚的最小代價,一個dp光求獲得足夠的停留的最小代價。
顯然由於這個題需要儲存的資訊很大,我們設狀態時就要思考如何才能在規定的時間內完成而不超時。
請求有5000個,若設與請求有關的狀態,例如:我們設f[i][j],表示當前在請求i,獲得的資格j的最小代價,確實,因為我們知道了請求i的所有資訊,包括結束時間,所在的旅館號的資訊,但在狀態轉移時,難免要列舉上一次的請求所在的位置,50005000,別忘了還有j的列舉再50,這樣就爆了,別報僥倖心理,這個題輸出方案加上你dp要做兩邊,常數巨大。那麼考慮重新設計狀態,考慮到轉移時,實際上若上一次的天數不為a[i].l,我們根本不用在意他在哪個旅館,當上一次的天數恰好為a[i].l時,我們才考慮他的旅館。那麼我們可以將天數和旅館都設在狀態裡,設f[i][j][k]表示在第i天在旅館j獲得的資格為k的最小代價。先列舉請求,然後考慮每個請求會使得哪些狀態發生改變,狀態轉移時,為了優化,我們令設dp[i][j]表示在第i天獲得資格j的最小代價,方便我們省去列舉的旅館數,之後我們另外討論天數為a[i].l的情況即可。複雜度為5000365
#include<bits/stdc++.h> using namespace std; int n,s[2],h,m,c[5005],num; int f[370][52][52][2],dp[370][52][2]; struct ss{int x,y,z;}Z[370][52][52][2],S[370][52][2]; //設f[i][j][k]表示第i天在j號旅館獲得的資格為k的最小代價, //dp[i][j]表示第i天獲得的資格為j的最小代價。 struct wy{int l,r,val,id,qid;}a[5005]; inline bool cmp(wy a,wy b) { return a.l<b.l; } inline void solve(int op) { f[0][0][0][op]=0;dp[0][0][op]=0; for(int i=1;i<=m;++i)//列舉所有的請求。 {//轉移來的天數分為兩類,一類小於l,一類等於l for(int j=0;j<a[i].l;++j)//列舉所有可以改變的狀態的天數 for(int k=0;k<=s[op];++k) { int d=min((op==0?k+a[i].r-a[i].l:k+1),s[op]); if(dp[j][k][op]+a[i].val<f[a[i].r][a[i].id][d][op]) { f[a[i].r][a[i].id][d][op]=dp[j][k][op]+a[i].val; Z[a[i].r][a[i].id][d][op]=S[j][k][op]; } if(f[a[i].r][a[i].id][d][op]<dp[a[i].r][d][op]) { dp[a[i].r][d][op]=f[a[i].r][a[i].id][d][op]; S[a[i].r][d][op]={a[i].r,a[i].id,d}; } } for(int j=0;j<=h;++j) //等於l的,列舉從那個旅館過來 { if(j==a[i].id) continue; for(int k=0;k<=s[op];++k) //列舉上一次的狀態。 { int d=min((op==0?k+a[i].r-a[i].l:k+1),s[op]); if(f[a[i].l][j][k][op]+a[i].val<f[a[i].r][a[i].id][d][op]) { f[a[i].r][a[i].id][d][op]=f[a[i].l][j][k][op]+a[i].val; Z[a[i].r][a[i].id][d][op]=(ss){a[i].l,j,k}; } if(f[a[i].r][a[i].id][d][op]<dp[a[i].r][d][op]) { dp[a[i].r][d][op]=f[a[i].r][a[i].id][d][op]; S[a[i].r][d][op]={a[i].r,a[i].id,d}; } } } } } inline void work(ss x,int op) { if(x.x==0||x.y==0||x.z==0) return; for(int i=1;i<=m;++i) { if(a[i].r==x.x&&a[i].id==x.y) { ss k=Z[x.x][x.y][x.z][op]; int d=min((op==0?k.z+a[i].r-a[i].l:k.z+1),s[op]); if(d==x.z&&f[x.x][x.y][x.z][op]-a[i].val==f[k.x][k.y][k.z][op]) { c[++num]=a[i].qid; work(k,op); return; } } } } int main() { //freopen("1.in","r",stdin); scanf("%d%d%d%d%d",&n,&s[0],&s[1],&h,&m); for(int i=1;i<=m;++i) { scanf("%d%d%d%d",&a[i].id,&a[i].l,&a[i].r,&a[i].val); a[i].qid=i; } sort(a+1,a+m+1,cmp); memset(f,0x3f,sizeof(f)); memset(dp,0x3f,sizeof(dp)); solve(0);solve(1); int ans1=1e9,ans2=1e9,o1,o2; for(int i=0;i<=n;++i) { if(dp[i][s[0]][0]<ans1) { ans1=dp[i][s[0]][0]; o1=i; } if(dp[i][s[1]][1]<ans2) { ans2=dp[i][s[1]][1]; o2=i; } } if(min(ans1,ans2)==1e9) {puts("IMPOSSIBLE");return 0;} else if(ans1<=ans2) work(S[o1][s[0]][0],0),puts("NIGHTS"); else if(ans1>ans2) work(S[o2][s[1]][1],1),puts("STAYS"); printf("%d\n",num); for(int i=num;i>=1;--i) printf("%d ",c[i]); return 0; }
人一旦有了自信,就擁有了一切!