線性dp(記憶化搜索)——cf953C(經典好題dag和dp結合)


非常好的題!和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'; 
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM