「美團 CodeM 初賽 Round B」送外賣2---------------狀壓dp
題目描述
一張 n 個點 m 條有向邊的圖上,有 q 個配送需求,需求的描述形式為 (si,ti,li,ri)( s_i , t_i , l_i , r_i )(si,ti,li,ri),即需要從點 si 送到 ti, 在時刻 li 之後(包括 lil_ili )可以在 sis_isi 領取貨物,需要在時刻 ri 之前(包括 ri)送達 ti ,每個任務只需完成一次。
圖上的每一條邊均有邊權,權值代表通過這條邊消耗的時間。在時刻 000 有一個工作人員在點 1 上,求他最多能完成多少個配送任務。
在整個過程中,可以認為領貨跟交貨都是不消耗時間的,時間只花費在路程上。當然在一個點逗留也是允許的。
輸出格式
一個整數,表示最多能完成的任務數量。
樣例輸入
5 4 3 1 2 1 2 3 1 3 4 1 4 5 1 1 2 3 4 2 3 1 2 3 4 3 4
樣例輸出
2
樣例解釋
工作人員可以在時刻 1 到達點 2 ,領取第二個貨物後在時刻 2 到達點 3 後交貨,逗留到時刻 4 ,領取第三個貨物,在時刻 4 到達點 4 並交貨。
• 首先的首先,需要明確配送的方式。在配送的途中手中不一定只有一份外賣!
然後出於對資料的敏感,易想到用進位制數表示狀態。
•
在dp陣列中作為一個維度,剩下時間和當前位置。
• 由於時間是資料中最大的存在,成為了dp的物件,當前位置成為了另一個狀
態維度。
• 轉移的話,只是合法性的判斷了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,q,x,y,z; 4 int dis[25][25]; 5 int f[60050][25],w,now,ans; 6 int base[15],s[15],t[15],l[15],r[15]; 7 int main() 8 { 9 base[0]=1; 10 for(int i=1;i<=11;++i) 11 base[i]=base[i-1]*3; 12 memset(dis,0x3f,sizeof(dis)); 13 memset(f,0x3f,sizeof(f));f[0][1]=0; 14 scanf("%d%d%d",&n,&m,&q); 15 for(int i=1;i<=m;++i) 16 { 17 scanf("%d%d%d",&x,&y,&z); 18 dis[x][y]=min(dis[x][y],z); 19 } 20 for(int i=1;i<=n;++i) 21 dis[i][i]=0; 22 for(int k=1;k<=n;++k) 23 for(int i=1;i<=n;++i) 24 for(int j=1;j<=n;++j) 25 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); 26 for(int i=0;i<q;++i) 27 scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]); 28 m=base[q]-1; 29 for(int i=0;i<=m;++i) 30 { 31 for(int j=1;j<=n;++j) 32 { 33 for(int k=0;k<q;++k) 34 { 35 now=i/base[k]%3; 36 if(now==0) 37 f[i+base[k]][s[k]]=min(f[i+base[k]][s[k]],max(f[i][j]+dis[j][s[k]],l[k])); 38 else if(now==1&&f[i][j]+dis[j][t[k]]<=r[k]) 39 f[i+base[k]][t[k]]=min(f[i+base[k]][t[k]],f[i][j]+dis[j][t[k]]); 40 41 } 42 if(f[i][j]<f[0][0]) 43 { 44 w=0; 45 for(int k=0;k<=10;++k) 46 if(i/base[k]%3==2) 47 ++w; 48 ans=max(ans,w); 49 } 50 } 51 } 52 printf("%d",ans); 53 return 0; 54 }程式碼1
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,q,x,y,z; 4 int dis[25][25]; 5 int f[60050][25],w,now,ans; 6 int base[15],s[15],t[15],l[15],r[15]; 7 int main() 8 { 9 base[0]=1; 10 for(int i=1;i<=11;++i) 11 base[i]=base[i-1]*3; 12 memset(dis,0x3f,sizeof(dis)); 13 memset(f,0x3f,sizeof(f));f[0][1]=0; 14 scanf("%d%d%d",&n,&m,&q); 15 for(int i=1;i<=m;++i) 16 { 17 scanf("%d%d%d",&x,&y,&z); 18 dis[x][y]=min(dis[x][y],z); 19 } 20 for(int i=1;i<=n;++i) 21 dis[i][i]=0; 22 for(int k=1;k<=n;++k) 23 for(int i=1;i<=n;++i) 24 for(int j=1;j<=n;++j) 25 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); 26 for(int i=0;i<q;++i) 27 scanf("%d%d%d%d",&s[i],&t[i],&l[i],&r[i]); 28 m=base[q]-1; 29 for(int i=0;i<=m;++i) 30 { 31 for(int j=1;j<=n;++j) 32 { 33 if(f[i][j]==f[0][0]) 34 continue; 35 for(int k=0;k<q;++k) 36 { 37 now=i/base[k]%3; 38 if(now==0) 39 f[i+base[k]][s[k]]=min(f[i+base[k]][s[k]],max(f[i][j]+dis[j][s[k]],l[k])); 40 else if(now==1&&f[i][j]+dis[j][t[k]]<=r[k]) 41 f[i+base[k]][t[k]]=min(f[i+base[k]][t[k]],f[i][j]+dis[j][t[k]]); 42 43 } 44 w=0; 45 for(int k=0;k<=10;++k) 46 if(i/base[k]%3==2) 47 ++w; 48 ans=max(ans,w); 49 } 50 } 51 printf("%d",ans); 52 return 0; 53 }程式碼2
這兩份程式碼在思路上是完全一致的。但是……
相似的結果還發生在另一個狀壓題上(第二份程式碼直接是TLE 了):
這究竟是種怎樣的操作?
如果當前狀態完全不可能被轉移到的話,就完全沒必要對它進行暴力擴充套件了,果斷continue。