先关题目
题目描述:
设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:
某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。
在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。
输入格式
第一行为一个整数N,表示 N×N 的方格图。
接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
行和列编号从 1开始,一行“0 0 0”表示结束。
输出格式
输出一个整数,表示两条路径上取得的最大的和。
数据范围
N≤10
输入样例:
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
输出样例:
67
思路
一般方法 四维dp
这个题要求的是走两遍,每次的最大值相加,选过的
方格值为0,一开始我想的是两次二维dp,选过的直接
为0,试验之后发现第二次的dp值为0,才想起来第一
此dp把所有的每格子可能的最大值全算出来了,也就是
说所有的格子都用过。
于是网上看到的常见的是四维dp,大概思路是这样
1.想象两个人在这个方格中走,每个人有一条路径,
总的就是两条路径,dp[i][j][k][l]表示第一个人
走到(i,j)这个格子的时候第二个走到(k,l)这
个格子的时候的最大值,答案就是dp[N][N][N][N],
正好表示第一个人走到(N,N)第二个人也走到了(N,N)
正好是两条路径;
2. 当两个人走到同一个格子的时候,格子的值只能加
一次,还有就是第一个人走到(i,j)有两种方法:要
么从上往下走走到了,要门从左往右走走到了,这样两
个人就有了“四种”状态,取这四种状态的最大值,就是
最后的答案;
代码
public class Main{
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int N=input.nextInt();
int [][]arr=new int[N+2][N+2];
for(int i=1;i<=12;i++){
int x=input.nextInt();
int y=input.nextInt();
arr[x][y]=input.nextInt();
}
int [][][][]dp=new int [N+1][N+1][N+1][N+1];
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
for(int k=1;k<=N;k++){
for(int l=1;l<=N;l++){
//第一个和第二个都是从下往上来
//第一个从上往下第二个从左往右
int temp1=Math.max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]);
//第一个左往右第二个从上往下
//第一个和第二个都是从左往右
int temp2=Math.max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]);
dp[i][j][k][l]=Math.max(temp1,temp2)+arr[i][j];
//坐标不相等时加两次
if(i!=k&&j!=l) dp[i][j][k][l]+=arr[k][l];
}
}
}
}
System.out.println(dp[N][N][N][N]);
}
}
闫氏DP分析法
y总讲的方法将四维DP转化成了三维DP,是因为我们要找的是走两遍可以看成,两个人同时走,同一个格子不能重复选择,所以同时走和分开走最后的结果是一样的。
同时走两条路线它们的横纵坐标的和是相等的,我们把这个和设为K,因此(i,j)就可以表示为(i,K-i)
dp[K][i1][i2]表示两个人从(1,1)分别走到(i1,K-i1)和(i2,K-i2)所有路径的最大值
这么说不好理解,直接上两张图
然后代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int N=input.nextInt();
int [][]arr=new int[N+2][N+2];
for(int i=1;i<=12;i++){
int x=input.nextInt();
int y=input.nextInt();
int z=input.nextInt();
if(x==0&&y==0&&x==0)
{
break;
}
else arr[x][y]=z;
}
//K表示横纵坐标之和走两条路,同时走所以x+y的值是一定的
//当i1==i2, 两个在同一格,就只需要加一次
int dp[][][]=new int [N*2+2][N+1][N+1];
for(int k=2;k<=N+N;k++){
for(int i1=1;i1<=N;i1++){
for(int i2=1;i2<=N;i2++){
int j1=k-i1;
int j2=k-i2;
//看是否符合要求
if(j1>=1&&j1<=N&&j2>=1&&j2<=N){
int t=arr[i1][j1];
if(i1!=i2)t+=arr[i2][j2];
int temp=dp[k][i1][i2];
temp=Math.max(temp,dp[k-1][i1-1][i2-1]+t);//上上
temp=Math.max(temp,dp[k-1][i1-1][i2]+t); //下右、
temp=Math.max(temp,dp[k-1][i1][i2-1]+t); //右下
temp=Math.max(temp,dp[k-1][i1][i2]+t); //右右
dp[k][i1][i2]=temp;
}
}
}
}
System.out.println(dp[N+N][N][N]);
}
}