/*
*1、实验内容:1)第1题:
*设平面上有一个m×n 的网格,将左下角的网格点标记为(0,0)而右上角的网格点标记为(m,n).某人想从(0,0)出发沿网格线行进到达(m,n).
*但是在网格点(i,j)处他只能向上行进或者向右行进,向上行进的代价为aij(aij=+∞),向右行进的代价是bij(bij=+∞)
*试设计一个动态规划算,在这个网格中为该旅行者寻找一条代价最小的旅行路线。编写一个简单的应用程序进行编译、运行;
*根据网上类似DP问题的思路,为了一致和方便,把左上角标记为起点(0,0),右下角为终点(m,n),每条路径的value(代价)设成了[1,10].
*/
import java.util.Scanner;
import java.util.Random;
public class ShortestPath
{
public static void main(String[] args)
{
//输入行数、列数,用m横n竖表示
Scanner matrix = new Scanner(System.in);
System.out.println("请输入行数m:");
int m = matrix.nextInt() + 1;
System.out.println("请输入列数n:");
int n = matrix.nextInt() + 1;
System.out.println("这是一个" + m + "横" + n + "竖的网格.\n");
//①产生一个↓ 的aij矩阵:m行n-1列,value[1,10]随机产生,并且打印.
System.out.println("①↓竖着走每一条线的值(aij)如下:");
int[][] aij = new int[m - 1][n];//矩阵aij,存放↓路径的value
for(int i = 0; i < m - 1; i++)
{
for(int j = 0; j < n; j++)
{
Random rand2 = new Random();
aij[i][j] =(rand2.nextInt(10) + 1);
System.out.printf("%3d",aij[i][j]);
}
System.out.println("\n");
}
//②同理产生一个 → 的bij矩阵:m-1行n列,value[1,10]随机产生,并且打印.
System.out.println("②→横着走每一条线的值(bij)如下:");
int[][] bij = new int[m][n - 1];//矩阵bij,存放→路径的value
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n - 1; j++)
{
Random rand1 = new Random();
bij[i][j] =(rand1.nextInt(10) + 1);
System.out.printf("%3d",bij[i][j]);
}
System.out.println("\n");
}
/*③
* 根据动态规划的设计,画出dp矩阵:
* 先打印第一横线的每个node的value;
* 再打印第一竖线的每个node的value;
* 然后,比较(根据上方入口node和对应的向下的abj,左侧入口node和向右的bij,补全dp矩阵.
* 最后,打印
*/
int[][] Dp = new int[m][n];//矩阵dp,存放每一node的最短路径值.
Dp[0][0] = 0;
for(int i = 1; i < m; i++)
{
Dp[i][0] = Dp[i - 1][0] + aij[i - 1][0];
}
for(int j = 1; j < n; j++)
{
Dp[0][j] = Dp[0][j - 1] + bij[0][j - 1];
}
for(int i = 0; i < m - 1; i++)
{
for(int j = 0; j < n - 1; j++)
{
Dp[i+1][j+1] = Math.min(Dp[i + 1][j] + bij[i + 1][j] ,Dp[i][j+1] + aij[i][j + 1]);
}
}
System.out.printf("③到达每个节点的最小值:\n");
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
System.out.printf("%5d",Dp[i][j]);
}
System.out.println(" \n");
}
/*
* 下面是输出到达节点的最优路径(从"终点"往"左上角起点"倒着再走一遍即可)
* 要找个数组s[k]记录每一次的倒着走的过程;要注意到达网格边缘的特殊情况
*/
Scanner node = new Scanner(System.in);
System.out.println("请输入交叉点位于第几横:");
int a = node.nextInt();
System.out.println("请输入交叉点位于第几竖:");
int b = node.nextInt();
if((a <= n) && (a >= 0) && (b <= m) && (b >= 0))//排除用户输入有误的情况
{
int [] s = new int [a + b - 1];//新建Step数组存放每一步是向下↓还是向右→,长度应该和下面的k一样,但是让k=0很麻烦,故比k长1.
int k = a + b - 2;//从起点到终点的步骤数目
int i = a - 1;
int j = b - 1;
while(i != 0 && j != 0)
{
if(Dp[i][j] == Dp[i][j - 1] + bij[i][j - 1])//判断(每一个)终点值来自何方,并用1/0记录.
{
s[k] = 0;//0标记为Right
j--;
k--;
}
else
{
s[k]= 1 ;//1标记为Down
i --;
k --;
}
if(i == 0)//走到第1横了,只能往左方向走(从右侧来).
{
while(j != 0)
{
s[k] = 0;
j --;
k --;
}
}
if(j == 0)//走到第1竖了,只能往上方向走(从下侧来).
{
while(i != 0)
{
s[k] = 1;
i --;
k --;
}
}
}
System.out.println("具体过程:");//按照上边用s[k]数组存的0/1,打印过程
for (i = 1; i <= a + b - 2 ; i ++ )
{
if ( s[i] == 0 )
System.out.println("Right→");
else
System.out.println("Down↓");
}
}
else
System.out.println("输入有误");
}
}