spoj703 Mobile Service---線性dp+狀態簡化
阿新 • • 發佈:2020-08-29
題目連結:https://vjudge.net/problem/SPOJ-SERVICE
好題。首先可以考慮設計一個思維狀態f[i][x][y][z]表示完成第i個,三個人分別位於x,y,z位置的最小費用,但是這樣dp的時間複雜度是O(nL^3)的。注意到完成第i個,一定有一個人的位置在Pi。於是可以只用剩下兩人的位置來定義狀態:f[i][j][k]表示完成第i個,除了位置在Pi的人以外,其他兩人的位置在j和k的最小費用,從階段i-1到階段i的狀態間,3個人都可以移動到Pi。有如下轉移方程:
f[i][j][k]=min(f[i][j][k],f[i-1][j][k]+c(Pi-1,Pi)),階段i-1中,j和k不動,位置Pi-1的人移到Pi
f[i][Pi-1][k]=min(f[i][Pi-1][k],f[i-1][j][k]+c(j,Pi)),階段i-1中,在Pi-1位置的人不動,位置j的人移到Pi
f[i][j][Pi-1]=min(f[i][j][Pi-1],f[i-1][j][k]+c(k,Pi)),階段i-1中,在Pi-1位置的人不動,位置k的人移到Pi
注意狀態的合法性,因為不能多個人在同一個位置(見下面程式碼寫狀態轉移時的很多if)。另外原題空間給的多,不用滾動陣列,但是空間如果限制到比如128mb就需要了,但是我滾到連樣例都沒過去......所以先給出一個不用滾動陣列的程式碼
#include<bits/stdc++.h> using namespace std; int c[210][210],p[1010],f[1010][210][210]; int t,n,l,i,j,k; int main(){ scanf("%d",&t); while (t--){ scanf("%d%d",&l,&n); for (i=1;i<=l;i++) for (j=1;j<=l;j++) scanf("%d",&c[i][j]); for (i=1;i<=n;i++) scanf("%d",&p[i]); memset(f,0x3f,sizeof(f)); f[0][1][2]=0; p[0]=3; for (i=1;i<=n;i++) for (j=1;j<=l;j++) for (k=1;k<=l;k++){ if (j==p[i-1]||k==p[i-1]||j==k) continue; //* if (j!=p[i]&&k!=p[i]) f[i][j][k]=min(f[i][j][k],f[i-1][j][k]+c[p[i-1]][p[i]]); if (k!=p[i]&&p[i-1]!=p[i]) f[i][p[i-1]][k]=min(f[i][p[i-1]][k],f[i-1][j][k]+c[j][p[i]]); if (j!=p[i]&&p[i-1]!=p[i]) f[i][j][p[i-1]]=min(f[i][j][p[i-1]],f[i-1][j][k]+c[k][p[i]]); } int ans=1e9; for (i=1;i<=l;i++) for (j=1;j<=l;j++) ans=min(ans,f[n][i][j]); printf("%d\n",ans); } return 0; }