动态规划算法练习:蓝桥杯,洛谷的传纸条游戏的三种解法

目录

 

方法一

方法二

方法三


方法一

package extraExercise;
/*
	算法训练 传纸条
	描述
	
	小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。
	一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,
	而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。
	幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,
	小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。
	从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
	
	在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。
	班里每个同学都可以帮他们传递,但只会帮他们一次,
	也就是说如果此人在小渊递给小轩纸条的时候帮忙,
	那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
	
	还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低
	注意:小渊和小轩的好心程度没有定义,输入时用0表示,
	可以用一个0-100的自然数来表示,数越大表示越好心。
	小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,
	即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。
	现在,请你帮助小渊和小轩找到这样的两条路径。
	
	输入
	输入描述:
	输入第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。
	
	接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。
	每行的n个整数之间用空格隔开。
	
	输入样例:
	3 3
	0 3 9
	2 8 5
	5 7 0
	
	输出
	输出描述:
	输出一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
	
	输出样例:
	34
 */

import java.util.Scanner;

public class TransferNote
{
	/*
	 * 问题可以简化成从开始点出发,找两条路线不一样的好心程度最大的路去终点
	 */
	public static void main(String[] args)
	{
		int[][] matrix = make();
		int n=matrix.length-1,m=matrix[0].length-1;
		/*
		 * ToHere四维数组,其中前面两个代表的是一条路到达(i,j)点的的最小路程,
		 * 后面两个代表的是另外一条路到达(k,l)的最小路程,而且两条路不会重复.
		 * 因为l的起始点和j不一样,l=j+1;所以l在列的位置是j的后一位开始,
		 * 这样子就保证了不可能线路重复,比如到达((2,2),(1,3)),如果这两个点都是从
		 * ((2,2),(2,2))出发的,到达((2,2),(2,2))这个点的好心值是0,因为这个点从来就没有被赋值过
		 * 还有因为两条路线的经过的人数是一样的,所以两条路线要同时同时走,这样就有四种可能
		 * ①右右②右下③下右④下下
		 * 对应四个代码是:
		 * ToHere[i][j-1][k-1][l],ToHere[i-1][j][k][l-1],ToHere[i][j-1][k][l-1],ToHere[i-1][j][k-1][l])
		 * 下一步就选前面一步中好心值最大的路线走
		 */
		int[][][][] ToHere=new int[n+1][m+1][n+1][m+1];
	    for (int i=1;i<=n;i++)
	    {
	    	for (int j=1;j<=m;j++)
	    	{
	    		for (int k=1;k<=n;k++)
	    		{
	    			for (int l=j+1;l<=m;l++)
	    			{
	    				ToHere[i][j][k][l]=
	    						Max(ToHere[i][j-1][k-1][l],ToHere[i-1][j][k][l-1],ToHere[i][j-1][k][l-1],ToHere[i-1][j][k-1][l])
	    						+matrix[i][j]+matrix[k][l];		                	
	    			}
	    		}
	    	}
	    }
	    //这里注意一下,必须是ToHere[n][m-1][n-1][m],
	    //到达的就是这两个位置,前面的到达(n,m-1),后面到达(n-1,m)
	    //前面到达(n-1,m)的话,后面就无法到达(n,m-1)
	    System.out.println(ToHere[n][m-1][n-1][m]);
	}
	
	//找最大的数
	public static int Max(int a,int b,int c,int d)
	{
	    return Math.max(Math.max(Math.max(a, b), c), d);
	}
	
	/*
	 * 构建这样的矩阵出来,因为动态规划问题第一行和第一列有意义地为0,
	 * 所以将矩阵构建多一行一列
	 */
	public static int[][] make()
	{
		int m,n;
		Scanner in = new Scanner(System.in);
		m=in.nextInt();
		n=in.nextInt();
		int[][] matrix=new int[m+1][n+1];
		for (int i = 1; i < matrix.length; i++)
		{
			for (int j = 1; j < matrix[i].length; j++)
			{
				matrix[i][j]=in.nextInt();
			}
		}
		in.close();
		return matrix;
	}
}

方法二

package extraExercise;

import java.util.Scanner;

public class TransferNote2
{
	
	public static void main(String[] args)
	{
		/*
		 * 这里将这个问题优化成三维的,因为之前的四维中发现,两个人走的步数是一样的
		 * 而且两个人的x和y坐标的和是相等的,而且画图你会发现在矩阵里面画多条右上角和左下角的
		 * 对角线,两个人的坐标肯定是在这些对角线的其中一条上面,所以现在就用
		 * XY表示对角线上的多个位置,XY的值就是对角线对应的x和y的坐标的和,
		 * 而x和y的值表示的是对角线上的两个位置要去的下一个位置的对应的横坐标,
		 * 如果横坐标是一样的,表示要去的位置是一样的,比如(5,3,3)表示就是从横纵坐标的和是5的两个
		 * 位置到达(3,3)这个位置的最大好感度
		 */
		int[][] matrix = make();
		
		int n=matrix.length-1,m=matrix[0].length-1;
		//这里的第一维大小必须大于等于n+m-1,因为最多都要走到坐标和是n+m-1的对角线
		int[][][] ToHere=new int[n+m][n+1][n+1];
		//XY可以直接从2开始,因为第一个数的坐标是(1,1),和就是2了
	    for (int XY=2;XY<n+m;XY++)
	    {
	    	for (int x=1;x<=n;x++)
	    	{
	    		for (int y=1;y<=n;y++)
	    		{
	    			//这里+1都是因为表示的是要去的下一个的位置,不符合输入矩阵的位置都可以不处理
	    			if(XY-x+1<1  || XY-y+1<1  ||XY-x+1>m  ||XY-y+1>m)
	    			{
	    				continue;
	    			}
	    			//这里和上一个方法一样,也是代表四种方法到达这个位置
    				ToHere[XY][x][y]=
    						Max(ToHere[XY-1][x][y],ToHere[XY-1][x-1][y-1],ToHere[XY-1][x-1][y],ToHere[XY-1][x][y-1])
    						+matrix[x][XY-x+1]+matrix[y][XY-y+1];		                	
    				/*
    				 * 如果相等的话就是去的是同一个地方,比如最后要去的(3,3)位置,它的好感度加一个
    				 * (3,3)位置的值就行了,所以减去一个,但是如果是中间的位置,比如(2,2)位置
    				 * 这时只加入一个值,到下面如果想走这条重复的路线好感度是小的,因为只加一个(一个是0)比随便
    				 * 加两个小
    				 */
    				if(x==y)
    				{
    					ToHere[XY][x][y] -=matrix[y][XY-y+1];
    				}
	    		}
	    	}
	    }
	    System.out.println(ToHere[n+m-1][n][n]);
	}
	
	//找最大的数
	public static int Max(int a,int b,int c,int d)
	{
	    return Math.max(Math.max(Math.max(a, b), c), d);
	}
	
	/*
	 * 构建这样的矩阵出来,因为动态规划问题第一行和第一列有意义地为0,
	 * 所以将矩阵构建多一行一列
	 */
	public static int[][] make()
	{
		int m,n;
		Scanner in = new Scanner(System.in);
		m=in.nextInt();
		n=in.nextInt();
		int[][] matrix=new int[m+1][n+1];
		for (int i = 1; i < matrix.length; i++)
		{
			for (int j = 1; j < matrix[i].length; j++)
			{
				matrix[i][j]=in.nextInt();
			}
		}
		in.close();
		return matrix;
	}
}

图如下

 

方法三

 

package extraExercise;

import java.util.Scanner;

public class TransferNote3
{
	
	public static void main(String[] args)
	{
		/*
		 * 这里和第二个有点区别的就是XY对角线上的位置代表的是到达该对角线上的
		 * 位置的最大好感度,x和y代表的是到达当前的位置,所以都不用和上面的方法一样-1
		 */
		int[][] matrix = make();
		
		int n=matrix.length-1,m=matrix[0].length-1;
		int[][][] ToHere=new int[n+m][n+1][n+1];
	    for (int XY=3;XY<n+m;XY++)
	    {
	    	for (int x=1;x<=n;x++)
	    	{
	    		for (int y=x+1;y<=n;y++)
	    		{
	    			if(XY-x<1  || XY-y<1  ||XY-x>m  ||XY-y>m)
	    			{
	    				continue;
	    			}
    				ToHere[XY][x][y]=
    						Max(ToHere[XY-1][x][y],ToHere[XY-1][x-1][y-1],ToHere[XY-1][x-1][y],ToHere[XY-1][x][y-1])
    						+matrix[x][XY-x]+matrix[y][XY-y];		                	
	    		}
	    	}
	    }
	    System.out.println(ToHere[n+m-1][n-1][n]);
	}
	
	//找最大的数
	public static int Max(int a,int b,int c,int d)
	{
	    return Math.max(Math.max(Math.max(a, b), c), d);
	}
	
	/*
	 * 构建这样的矩阵出来,因为动态规划问题第一行和第一列有意义地为0,
	 * 所以将矩阵构建多一行一列
	 */
	public static int[][] make()
	{
		int m,n;
		Scanner in = new Scanner(System.in);
		m=in.nextInt();
		n=in.nextInt();
		int[][] matrix=new int[m+1][n+1];
		for (int i = 1; i < matrix.length; i++)
		{
			for (int j = 1; j < matrix[i].length; j++)
			{
				matrix[i][j]=in.nextInt();
			}
		}
		in.close();
		return matrix;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReflectMirroring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值