題目鏈接: https://codeforces.com/contest/983/problem/C
題意:
你現在控制電梯要搭載 個人上下客,這些人按照先來後到的順序依次進入電梯(但不一定要先進電梯的先出),你已知的是每個人上電梯和下電梯的樓層,問你令所有人都走出電梯的最少時間是多少。
做法:
以下是原來的錯誤想法…可以跳過,自己寫寫留個教訓。
一開始的時候沒考慮周到,因爲很明顯是個 ,定義的狀態是 表示已經走掉了 個人,當前電梯裏還有 個人,所處樓層爲 的最少時間,本來想着可以直接 出來答案的,後來發現錯了,因爲在相同的 和 的狀態下,雖然下一個進來的人是固定的,但是可能走出的人是不同的,所以直接這樣定義最少時間是不準確的…
然後就想着說,把當前電梯裏的每個人的目標樓層都記錄下來,但是很明顯 這樣的狀態肯定是會炸掉的。
然後就看到了優化,因爲如果人數已經到達了四個人,那麼下一個肯定要去往某個人的目的地把人放下才能繼續上人,所以我們可以馬上處理已經有三個人的情況,把其中一個人的一維優化掉。
不免感慨記憶化搜索的強大啊…從後往前這樣的方法還是比較少用到的,多看多學多敲…
代碼
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep_e(i,u) for(int i=head[u];~i;i=nex[i])
using namespace std;
typedef long long ll;
const int maxn=2005;
int dp[maxn][10][10][10][10],n,in[maxn],ou[maxn];
int cal(int u,int v){ return abs(u-v);}
int dfs(int p,int f,int a,int b,int c){
if(dp[p][f][a][b][c]!=-1) return dp[p][f][a][b][c];
int ret=1e9;
//進倉人數到達上限
if(p==n){
if(!a&&!b&&!c) return 0;
if(a) ret=min(ret,dfs(p,a,0,b,c)+cal(f,a)+1);
if(b) ret=min(ret,dfs(p,b,a,0,c)+cal(f,b)+1);
if(c) ret=min(ret,dfs(p,c,a,b,0)+cal(f,c)+1);
return dp[p][f][a][b][c]=ret;
}
//有人走
if(a) ret=min(ret,dfs(p,a,0,b,c)+cal(f,a)+1);
if(b) ret=min(ret,dfs(p,b,a,0,c)+cal(f,b)+1);
if(c) ret=min(ret,dfs(p,c,a,b,0)+cal(f,c)+1);
//還有空餘位置有人上
int v=p+1;
if(!a) ret=min(ret,dfs(p+1,in[v],ou[v],b,c)+cal(f,in[v])+1);
else if(!b) ret=min(ret,dfs(p+1,in[v],a,ou[v],c)+cal(f,in[v])+1);
else if(!c) ret=min(ret,dfs(p+1,in[v],a,b,ou[v])+cal(f,in[v])+1);
else{
ret=min(ret,dfs(p+1,a,ou[v],b,c)+cal(f,in[v])+cal(in[v],a)+2);
ret=min(ret,dfs(p+1,b,a,ou[v],c)+cal(f,in[v])+cal(in[v],b)+2);
ret=min(ret,dfs(p+1,c,a,b,ou[v])+cal(f,in[v])+cal(in[v],c)+2);
ret=min(ret,dfs(p+1,ou[v],a,b,c)+cal(f,in[v])+cal(in[v],ou[v])+2);
}
return dp[p][f][a][b][c]=ret;
}
int main(){
memset(dp,-1,sizeof(dp));
scanf("%d",&n);
rep(i,1,n){
scanf("%d%d",&in[i],&ou[i]);
}
printf("%d\n",dfs(0,1,0,0,0));
return 0;
}