• 线性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)<<'
    ';
    }

     此外是滚动数组的版本(没有降维复杂度比较高)

    #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<<'
    '; 
    }
  • 相关阅读:
    JavaScript获取后台C#变量以及后台方法
    无刷新二级联动dropdownlist
    如何使用验证控件对DropDownList进行验证
    对某项目中Vuex用法的分析
    JavaScript中的深拷贝和浅拷贝
    Vue开发中的中央事件总线
    Vue项目中使用Vuex + axios发送请求
    html文件引用本地js文件出现跨域问题的解决方案
    数组的一些与遍历相关的方法总结
    《EMCAScript6入门》读书笔记——16.Generator函数的语法
  • 原文地址:https://www.cnblogs.com/zsben991126/p/11173126.html
Copyright © 2020-2023  润新知