983 Codeforces Round #483 (Div. 1) C. Elevator dp

題目鏈接: https://codeforces.com/contest/983/problem/C

題意:

你現在控制電梯要搭載 nn 個人上下客,這些人按照先來後到的順序依次進入電梯(但不一定要先進電梯的先出),你已知的是每個人上電梯和下電梯的樓層,問你令所有人都走出電梯的最少時間是多少。

做法:

以下是原來的錯誤想法…可以跳過,自己寫寫留個教訓。

一開始的時候沒考慮周到,因爲很明顯是個 dpdp ,定義的狀態是 dp[i][j][k]dp[i][j][k] 表示已經走掉了 ii 個人,當前電梯裏還有 jj 個人,所處樓層爲 kk 的最少時間,本來想着可以直接 bfsbfs 出來答案的,後來發現錯了,因爲在相同的 iijj 的狀態下,雖然下一個進來的人是固定的,但是可能走出的人是不同的,所以直接這樣定義最少時間是不準確的…

然後就想着說,把當前電梯裏的每個人的目標樓層都記錄下來,但是很明顯 dp[n=2e4][10][10][10][10][floor=10]dp[n=2e4][10][10][10][10][floor=10] 這樣的狀態肯定是會炸掉的。

然後就看到了優化,因爲如果人數已經到達了四個人,那麼下一個肯定要去往某個人的目的地把人放下才能繼續上人,所以我們可以馬上處理已經有三個人的情況,把其中一個人的一維優化掉。

不免感慨記憶化搜索的強大啊…從後往前這樣的方法還是比較少用到的,多看多學多敲…

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章