第一次写博客文章,有点小紧张。若是有什么错误还望众大神指点。为了备战下个月的蓝桥杯,苦战算法题,觉得有一道题不错,就拿来分享一下。
原文如下:地宫取宝,X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。地宫的入口在左上角,出口在右下角。小明被带到地宫的入口,国王要求他只能向右或向下行走。走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
【数据格式】输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值,要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。
例如,输入:
2 2 2
1 2
2 1
程序应该输出:2
再例如,输入:
2 3 2
1 2 3
2 1 5
程序应该输出:14(题目有点长)
第一次看到这题目时,一个头两个大,卧槽,这么复杂的过程,而且当时还没有学算法。不过把题目看多几遍,慢慢的就看懂了他的意思。比如 格子矩阵为
A B C
D E F 那么小明可走的路线有 ABCF,ABEF,ADEF三种。而每一条路线又有不同的取法。所以我认为这道题可以分为渐进的两个步骤,一是找出所有小明可走的路线,而是算出每条路线可行的取宝方法数。 首先,对于第一个步骤,我们可以采用递归的方法,用动态数组(初始化动态数组的第一个值为0,原因在第二个步骤)记录路线每一格子的宝物价值,我创建了一个go方法,带有两个参数分别为当前格子坐标,在main方法里面调用go(0,0)开始取宝。go方法如下:
public static void go(int i,int j){ //采用递归的方法做出类似树的遍历. i为横坐标,j为纵坐标,比如上面B的坐标为(0,1)
newList.add(r[i][j]); //为动态数组添加宝物价值
if(i==n-1 && j<m-1){ //如果i==n-1 && j<m-1,即走到上面的C,那么小明只能向下走
go(i,j+1);
}else if(j==m-1 && i<n-1){ //如果j==m-1 && i<n-1,即走到上面D,的那么小明只能向右走
go(i+1,j);
}
else if(i==n-1&&j==m-1){ //如果到达最后一个格子,那么就进行对这条路线取值方法分析
run(0); //进行线路取值分析-->run (即第二个步骤)
}else{ //如果j<m-1 && i<n-1,那么小明可以向右走也可以向下走
go(i,j+1);
go(i+1,j);
}
newList.remove(newList.size()-1); //当每一条路线分析完后,移除路线的最后一步。动态数组循环利用.不用为每一条路线声明一个动态数组
}
这样第一个步骤就完成了, 第二个步骤:题目给出的第二个例子中有一条路线为1235,k为2。哎,还是直接上代码比较好说:
public static void run(int num){
for(int i = num+1;i<newList.size();i++){
if(max<newList.get(i)){ //动态数组第一个值为零为了更好的使用递归,这样第个格子就可以比较
p++; //p为记录当前小明取的宝物数,若当前格子宝物价值大于之前的拿的宝物,就拿,
if(p==k){ //所取宝物数等于k那么count(总的方法数加一)
count++;
}else if(i==newList.size()-1){ //路线走完
}else{ //进行下一步
max = newList.get(i);
run(i);
}
p--; // 可表示不取
}
}
完整代码
import java.util.Scanner;
public class Main {
public static Scanner read = new Scanner(System.in);
static int n,m,k; //n表示矩阵的行数,m表示矩阵的列数,k表示小明所需要的宝物数
static int[][] r ; //用二维数组表示n*m的矩阵
static ArrayList<Integer> newList = new ArrayList(); //用动态数组储存每一步对应格子的宝物价值
static int p = 0,max = 0; // p表示当前小明拿的宝物数
private static int count = 0; //count表示方法数
public static void main(String[] args){
n = read.nextInt();m = read.nextInt();k = read.nextInt();
r = new int[n][m];
read.nextLine(); //读取下一行
for(int i = 0; i<n ;i++){
for(int j = 0; j<m ;j++){
r[i][j] = read.nextInt();
}
read.nextLine();
} //给矩阵附上对应的宝物价值
go(0,0); //开始取宝物啦 ,0,0)这是每一步必走的格子
System.out.print(count); //输出方法数
}
public static void go(int i,int j){ //采用递归的方法做出类似树的遍历
newList.add(r[i][j]);
if(i==n-1 && j<m-1){ //如果i==n-1 && j<m-1,那么小明只能向下走
go(i,j+1);
}else if(j==m-1 && i<n-1){ //如果j==m-1 && i<n-1,那么小明只能向右走
go(i+1,j);
}else if(i==n-1&&j==m-1){ //如果到达最后一个格子,那么就进行对这条路线取值方法分析
max = 0;
run(0); //进行线路取值分析-->run
}else{ //如果j<m-1 && i<n-1,那么小明可以向右走也可以向下走
go(i,j+1);
go(i+1,j);
}
newList.remove(newList.size()-1); //当每一条路线分析完后,移除路线的最后一步
}
public static void run(int num){
for(int i = num+1;i<newList.size();i++){
if(max<newList.get(i)){
p++;
if(p==k){
count++;
}else if(i==newList.size()-1){
}else{
max = newList.get(i);
run(i);
}
p--;
}
}
}