zoukankan      html  css  js  c++  java
  • dp--四维模板&空间压缩&洛谷P1004,P1006题解

    1.0题目

    1.1 P1004

    https://www.luogu.com.cn/problem/P1004

    1.2 P1006

    https://www.luogu.com.cn/problem/P1006

    2.0讲解

    2.1 P1004

    题目很明显,就是从A到B点走两次,问路径最大值。
    这个题允许交叉路径。
    第一个错误思路是从左上走到右下,两次DP取最大值。
    但是这两次路径的选择互相影响,但我们设计的状态并没有估计到,所以显然是错误的,例如下面这个例子:

    0 0 1 0
    0 0 1 0
    1 0 1 1
    1 0 2 0
    

    所以,我们不妨设是有两个人同时从左上角开始走,走到右下角,这样可以满足这两条路径互相影响,第一个人走的路径考虑到了第二个人。
    很明显可以设思维状态表示两个人的坐标,但这样太浪费空间,
    所以可以设三维状态 (F_{i,j,k}) 表示第一个人走到i行,第二个人走到j行,一共走了k步时的最优解。其中i,j均小于(min(k,n))
    转移方程很明显是由上一步得到,那么是从上面走下来,还是从左面走过来,算上两个人,一共是有4种情况。
    转移方程为(F_{i,j,k}=max(F_{i,j,k-1},F_{i-1,j,k-1},F_{i-1,j-1,k-1},F_{i,j-1,k-1}))
    进行dp,如果初始状态算一步的话,那么结果应该是(F_{n,n,2*n-1})
    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #include<deque>
    #include<cstdlib>
    #include<ctime>
    #define dd double
    #define ll long long
    #define ull unsigned long long
    #define N 10
    #define M number
    using namespace std;
    
    int f[N][N][N*2],n;
    int a[N][N],x,y,val;
    
    inline int Max(int a,int b){
    	return a>b?a:b;
    }
    
    int main(){
    	scanf("%d",&n);
    	scanf("%d%d%d",&x,&y,&val);
    	while(x!=0){
    		a[x][y]=val;
    		scanf("%d%d%d",&x,&y,&val);
    	}
    	for(int i=1;i<=2*n-1;i++){
    		for(int j=1;j<=i&&j<=n;j++){
    			for(int k=1;k<=i&&k<=n;k++){
    				int jy=i-j+1,ky=i-k+1;
    				f[j][k][i]+=Max(f[j][k][i-1],Max(Max(f[j][k-1][i-1],f[j-1][k][i-1]),f[j-1][k-1][i-1]))+a[j][jy]+a[k][ky];
    				if(j==k) f[j][k][i]-=a[j][jy];
    			}
    		}
    	}
    	printf("%d",f[n][n][2*n-1]);
    	return 0;
    }
    

    但其实空间上可以再次优化,我们发现所有的(F_{i,j,k})都是由(F_{q,p,k-1})转移得到,所以可以用类似01背包的方式把空间压缩到两维,同时需要改变循环顺序。
    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #include<deque>
    #include<cstdlib>
    #include<ctime>
    #define dd double
    #define ll long long
    #define ull unsigned long long
    #define N 10
    #define M number
    using namespace std;
    
    int f[N][N],n;
    int a[N][N],x,y,val;
    
    inline int Max(int a,int b){
    	return a>b?a:b;
    }
    
    inline int Min(int a,int b){
    	return a<b?a:b;
    }
    
    int main(){
    	scanf("%d",&n);
    	scanf("%d%d%d",&x,&y,&val);
    	while(x!=0){
    		a[x][y]=val;
    		scanf("%d%d%d",&x,&y,&val);
    	}
    	for(int i=1;i<=2*n-1;i++){
    		for(int j=Min(i,n);j>=1;j--){
    			for(int k=Min(i,n);k>=1;k--){
    				int jy=i-j+1,ky=i-k+1;
    				f[j][k]=Max(f[j][k],Max(Max(f[j][k-1],f[j-1][k]),f[j-1][k-1]))+a[j][jy]+a[k][ky];
    				if(j==k) f[j][k]-=a[j][jy];
    			}
    		}
    	}
    	printf("%d",f[n][n]);
    	return 0;
    }
    

    2.2 P1996

    这个题和上一个题基本类似,但是唯一不同的是不允许路径交叉。
    这里配合代码讲解
    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #include<deque>
    #include<cstdlib>
    #include<ctime>
    #define dd double
    #define ll long long
    #define ull unsigned long long
    #define N 51
    #define M number
    using namespace std;
    
    int m,n;
    int a[N][N],f[N][N][N*2];
    
    inline int Max(int a,int b){
    	return a>b?a:b;
    }
    
    inline int Min(int a,int b){
    	return a>b?b:a;
    }
    
    int main(){
    	scanf("%d%d",&m,&n);
    	for(int i=1;i<=m;i++)
    	    for(int j=1;j<=n;j++)
    	        scanf("%d",&a[i][j]);
    	
    	for(int i=1;i<=n+m-1;i++){
    		for(int j=1;j<=m&&j<=i;j++){
    			for(int k=1;k<=m&&k<=i;k++){
    				if(j==k) continue;
    				int jy=i-j+1,ky=i-k+1;
    				f[j][k][i]=Max(f[j][k][i-1],Max(f[j-1][k][i-1],Max(f[j][k-1][i-1],f[j-1][k-1][i-1])))+a[j][jy]+a[k][ky];
    			}
    		}
    	}
    	
    	printf("%d",Max(f[m][m][m+n-1],Max(f[m-1][m][m+n-1],Max(f[m][m-1][m+n-1],f[m-1][m-1][m+n-1]))));
    }
    

    其中循环中jk是为了防止路径交叉的情况,转移方程基本类似。
    还有一个不同点是输出结果时,因为当i
    j时并没有更新答案,所以最右下角并没有更新,需要再加一步取最优值。

  • 相关阅读:
    如何说明白代码评审
    面试感悟----一名3年工作经验的程序员应该具备的技能(转载自@五月的仓颉)
    根据ip地址从第三方接口获取详细的地理位置
    linux安装telnet遇到的问题
    redis脑图
    数据库相关面试题
    logback系列一:名词解释
    java并发编程系列一、多线程
    logback系列二:logback在项目中的应用
    rocketmq特性(features)
  • 原文地址:https://www.cnblogs.com/TianMeng-hyl/p/14120889.html
Copyright © 2011-2022 走看看