非常好的題!和spoj 的 Mobile Service有點相似,用記憶化搜索很容易解決
看了網上的題解,也是減掉一維,剛好可以開下數組 https://blog.lucien.ink/archives/224/
#include<bits/stdc++.h> using namespace std; #define maxn 2005 int n,A[maxn],B[maxn]; int dp[maxn][11][11][11][11]; //狀態:准備去拉第i個人,當前在cur樓,另外三個人的目標樓層是abc int dfs(int i,int cur,int a,int b,int c){ if(dp[i][cur][a][b][c]!=-1) return dp[i][cur][a][b][c]; int res=0x3f3f3f3f; if(i>n){//終止狀態,只要把abc送到終點即可 if(!a && !b && !c)res=0; if(a!=0)res=min(res,dfs(i,a,0,b,c)+abs(cur-a)+1);//送到a if(b!=0)res=min(res,dfs(i,b,a,0,c)+abs(cur-b)+1);//送到b if(c!=0)res=min(res,dfs(i,c,a,b,0)+abs(cur-c)+1);//送到c return dp[i][cur][a][b][c]=res; } //先放下abc的決策 if(a)res=min(res,dfs(i,a,0,b,c)+abs(cur-a)+1); if(b)res=min(res,dfs(i,b,a,0,c)+abs(cur-b)+1); if(c)res=min(res,dfs(i,c,a,b,0)+abs(cur-c)+1); //准備去拉一個人的決策:先去把i接上電梯 if(a&&b&&c){//電梯全滿,再拉一個人需要先放下一個人 res=min(res,dfs(i+1,B[i],a,b,c)+abs(cur-A[i])+abs(A[i]-B[i])+2); res=min(res,dfs(i+1,a,B[i],b,c)+abs(cur-A[i])+abs(A[i]-a)+2); res=min(res,dfs(i+1,b,a,B[i],c)+abs(cur-A[i])+abs(A[i]-b)+2); res=min(res,dfs(i+1,c,a,b,B[i])+abs(cur-A[i])+abs(A[i]-c)+2); } else {//先去接i,再拉一個人 if(!a)res=min(res,dfs(i+1,A[i],B[i],b,c)+abs(cur-A[i])+1); else if(!b)res=min(res,dfs(i+1,A[i],a,B[i],c)+abs(cur-A[i])+1); else if(!c)res=min(res,dfs(i+1,A[i],a,b,B[i])+abs(cur-A[i])+1); } return dp[i][cur][a][b][c]=res; } int main(){ memset(dp,-1,sizeof dp); cin>>n; for(int i=1;i<=n;i++)cin>>A[i]>>B[i]; cout<<dfs(1,1,0,0,0)<<'\n'; }
此外是滾動數組的版本(沒有降維復雜度比較高)
#include<bits/stdc++.h> using namespace std; #define maxn 2005 int n,a[maxn],b[maxn],dp[2][11][11][11][11][11]; void calc(int &a,int b){ int tmp=min(a,b); a=tmp; } int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]>>b[i]; memset(dp,0x3f,sizeof dp); int cur=0; dp[cur][1][0][0][0][0]=2*n;//開始停在1樓,因為n個人總共上下2*n次,所以直接加上這個值 for(int i=0;i<=n;i++){//去接第i+1個人 for(int x=9;x>=0;x--)//這里必須逆序,因為把電梯里的人放下時的目標狀態是dp[cur][][0][0][0][0],即消除掉后效性 for(int y=9;y>=0;y--) for(int z=9;z>=0;z--) for(int w=9;w>=0;w--) for(int f=1;f<=9;f++){ int now=dp[cur][f][x][y][z][w]; if(now==0x3f3f3f3f)continue; if(x==0 && i<n)//把第i+1個人放在位置x,更新狀態到下一步 calc(dp[cur^1][a[i+1]][b[i+1]][y][z][w],now+abs(f-a[i+1])); else if(x)//不接人把電梯里的人送到目的地 calc(dp[cur][x][0][y][z][w],now+abs(f-x)); if(y==0 && i<n) calc(dp[cur^1][a[i+1]][x][b[i+1]][z][w],now+abs(f-a[i+1])); else if(y) calc(dp[cur][y][x][0][z][w],now+abs(f-y)); if(z==0 && i<n) calc(dp[cur^1][a[i+1]][x][y][b[i+1]][w],now+abs(f-a[i+1])); else if(z) calc(dp[cur][z][x][y][0][w],now+abs(f-z)); if(w==0 && i<n) calc(dp[cur^1][a[i+1]][x][y][z][b[i+1]],now+abs(f-a[i+1])); else if(w) calc(dp[cur][w][x][y][z][0],now+abs(f-w)); } if(i<n){ memset(dp[cur],0x3f,sizeof dp[cur]); cur^=1; } } int ans=0x3f3f3f3f; for(int i=1;i<=9;i++) ans=min(ans,dp[cur][i][0][0][0][0]); cout<<ans<<'\n'; }