深搜之迷宫类


常见的迷宫类搜索可以分为以下5种类型

1. 找一组可行解(判断是否可行)
2. 所有可行解
3. 最优解(通常见最少步数)
4. 计算连通块数目
5. 求最大连通块

找一组可行解 可以不取消标记(下次不同路径到达这个点还是不可行) 找到一组解即返回结果。但如果是找所有可行解、路径、最小步数一定要取消标记

一、一组可行解(判断是否可行)

迷宫游戏

/*题目描述
给一个n行m列的迷宫,'S'表示迷宫的起点,'T'表示迷宫的终点,
'*'表示不能通过的点,'.'表示可以通过的点。现在要求从'S'出发走到'T',
每次只能上下左右走动,并且只能进入能通过的点,每个点只能通过一次,
问是否能够到达终点,如果不可以输出"NO!",可以则打印其中一条路径。
测试数据:
5 6
....S*
.***..
.*..*.
*.***.
.T....

答案:
....m*
.***mm
.*..*m
*.***m
.Tmmmm
 */
package 迷宫类可行解;

import java.util.Scanner;
//使用方向数组 for循环
public class 迷宫游戏2 {
	private static int n;
	private static int m;
	static char[][] maze;
	static boolean[][] visited;
	//方向数组 分别表示上 左 下 右
	static int[][] dire= {{-1,0},{0,-1},{1,0},{0,1}};
	//判断是否在迷宫内
	private static boolean in(int x,int y) {
		return 0<=x&x<n&0<=y&y<m;
	}

	static boolean dfs(int x,int y) {
	//递归出口
		if(maze[x][y]=='T') {
			return true;
		}
		int dx,dy;
		//标记(x,y)已经走过
		maze[x][y]='m'; 
		visited[x][y]=true;
		for(int i=0;i<4;i++) {//在maze[2][0]处往四个方向尝试均失败 后执行后面代码将maze[2][0]恢复为'.' 
			dx=x+dire[i][0];
			dy=y+dire[i][1];
			if(in(dx,dy)&&maze[dx][dy]!='*'&&!visited[dx][dy]) {
				if(dfs(dx,dy)) {//找到出口
					return true;	//返回true到 最近调用函数dfs()
				}
			}
		}
		//回溯代码
		maze[x][y]='.';
		visited[x][y]=false;//找一组可行解 可以不取消标记(下次不同路径到达这个点还是不可行) 找到一组解即返回结果
		return false;
	}
	//打印路径
	private static void show(char[][] array) {
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				System.out.print(array[i][j]);
			}
			System.out.println();
		}
		
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		n=reader.nextInt();
		m=reader.nextInt();
		int x = 0,y = 0;//局部变量需初始化
		maze=new char[n][m];
		visited=new boolean[n][m];
		for (int i = 0; i <n; i++) {
			String s=reader.next();
			for (int j = 0; j < m; j++) {
				if(s.charAt(j)=='S') {
				//先找到入口位置
					x=i;
					y=j;}
				maze[i][j]=s.charAt(j);
			}
		}
		if(dfs(x,y)) {
			show(maze);
		}
		else
			System.out.println("NO!");

	}



}

注意
如果在当前层(x,y)找到一个可以扩展的点,则当前层会在回溯前return true,因此回溯取消标记的代码并不会被执行。只有在四个扩展均尝试失败了,当前层(x,y)才会被回溯取消标记。

for(int i=0;i<4;i++) {//在maze[2][0]处往四个方向尝试均失败 后执行后面代码将maze[2][0]恢复为'.' 
		dx=x+dire[i][0];
		dy=y+dire[i][1];
		if(in(dx,dy)&&maze[dx][dy]!='*'&&!visited[dx][dy]) {
			if(dfs(dx,dy)) {//找到出口, 后面回溯的代码不会执行
				return true;	//返回true到 最近调用函数dfs()
			}
		}
	}
//回溯代码
maze[x][y]='.';
visited[x][y]=false;//找可行解 可以不取消标记(下次不同路径到达这个点还是不可行) 找到一组解即返回结果
return false;

王子救公主(判断是否可行)

 /*题目描述
一天,蒜头君梦见自己当上了王子,但是不幸的是,自己的公主被可恶的巫婆抓走了。
于是蒜头君动用全国的力量得知,自己的公主被巫婆抓进一个迷宫里面。
由于全国只有蒜头君自己可以翻越迷宫外的城墙,蒜头君便自己一人走上的拯救自己公主的路途。
碰巧的是巫婆出去了,迷宫也不大,蒜头君可以直接和公主对话,于是两个人便开始相互靠近。
每一步移动只能朝着上下左右四个方向走一格,不能走进墙所在的位置。
蒜头君救公主心切,一次必须沿着一个方向走两步(允许跨越迷宫中的墙);
公主柔弱,一次只能走一步。问在这个迷宫中,蒜头君是否可以救出公主(
蒜头君和公主相遇后,就能背着公主逃出迷宫了)。

输入格式
第一行输入两个整数 n(1≤n≤100), m(1≤m≤100) 表示迷宫的行和列。
然后有一个 n×m 的地图,地图由'.'、'#'、'w'、'g'这四个部分组成。
'.'表示可以通行的路,'#'表示迷宫的墙,'w'表示王子开始所在的位置,'g'表示公主开始所在的位置。

输出格式
输出王子是不可以救出自己的公主,如果能救出则输出"yes",否则输出"no"。

测试数据:
1 8
w....#.g 
答案:Yes
 */

思路:dfs分别搜处蒜头君可以到哪些点 公主可以到哪些点,有重合的点即他们会相遇

package 迷宫类可行解;

import java.util.Scanner;

public class 蒜头君救公主 {
	static int n,m;
	static boolean vis[][][];
	static int map[][];
	private static void dfs(int x,int y,int d) {
		//d为0是表示是蒜头君步长为2-0 为1则为公主步长为2-1
		//将越界、访问过、墙壁的位置这些非法位置return掉
		if(x<0||x>=n||y<0||y>=m||vis[x][y][d]||map[x][y]=='#') {
			return;
		}
		//标记访问过
		vis[x][y][d]=true;
		//分别往四个方向搜索
		dfs(x-(2-d),y,d);
		dfs(x+(2-d),y,d);
		dfs(x,y-(2-d),d);
		dfs(x,y+(2-d),d);
		//由于是求可行解 不需要回溯取消标记
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		int x=0,y=0,x1=0,y1=0;//局部变量调用需要初始化
		boolean flag = false;
		n=reader.nextInt();
		m=reader.nextInt();
		vis=new boolean[n][m][2];
		map=new int[n][m];
		for (int i = 0; i <n; i++) {
			String s=reader.next();
			for(int j=0;j<m;j++) {
				if(s.charAt(j)=='w') {
					x=i;
					y=j;
				}
				else if(s.charAt(j)=='g') {
					x1=i;
					y1=j;
				}
				map[i][j]=s.charAt(j);
			}
		}
		dfs(x,y,0);
		dfs(x1,y1,1);
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				if(vis[i][j][0]&&vis[i][j][1]) {//有交集则可以救出
					flag=true;
				}
			}
		}
		if(flag)
			System.out.println("Yes");
		else
			System.out.println("No");

	}

}

二、所有可行解

迷宫的方案数

/*
给一个n行m列的迷宫,'S'表示迷宫的起点,'e'表示迷宫的终点,
'#'表示不能通过的点,'.'表示可以通过的点。现在要求从'S'出发走到'e',
每次只能上下左右走动,并且只能进入能通过的点,每个点只能通过一次,
现在要求你求出有多少种通过迷宫的方案。
输入:
第一行输入行数n和列数m
接下来n行输入n行m列的迷宫

输出:
通过迷宫的方案数目

测试数据
5 5
S..# 
.#.# 
.#.# 
.#...
....e
结果:8

测试数据二
5 5
S# # 
.# # 
.# # 
.# # 
....e
结果:1
 */
package 迷宫类可行解;

import java.util.Scanner;

public class 迷宫解的方案数 {
	private static int n;
	private static int m;
	private static int count=0;
	static char[][] maze;
	static boolean[][] visited;
	//上 左 下 右
	static int[][] dire= {{-1,0},{0,-1},{1,0},{0,1}};
	
	private static boolean in(int x,int y) {
		return 0<=x&&x<n&&0<=y&&y<m;
	}

	static void dfs(int x,int y) {
		System.out.println(x+" "+y+" "+count);
		if(maze[x][y]=='e') {
			count++;
			show(maze);//应该在这里输出 走的路线
			return;
		}
		int dx,dy;
		visited[x][y]=true;//访问之后标记为true
		//maze[x][y]='m';
		for(int i=0;i<4;i++) {//以(x,y)为起点往四个方向走
			dx=x+dire[i][0];
			dy=y+dire[i][1];
			if(in(dx,dy)&&maze[dx][dy]!='#'&&!visited[dx][dy]) {
				//maze[dx][dy]='m';		//错误写法		
				dfs(dx,dy);			
				//maze[dx][dy]='.';
				//每次一return 后面的visited[x][y]=false;就不会执行 不会被修改过来
			}
		}
		//maze[x][y]='.';
		visited[x][y]=false;//找全部解需取消标记 否则不同方案经过的公共点会被标记为true
	}
	private static void show(char[][] array) {
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				System.out.print(array[i][j]);
			}
			System.out.println();
		}
		
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		n=reader.nextInt();
		m=reader.nextInt();
		int x = 0,y = 0;//局部变量需初始化
		maze=new char[n][m];
		visited=new boolean[n][m];
		for (int i = 0; i <n; i++) {
			String s=reader.next();
			for (int j = 0; j < m; j++) {
				if(s.charAt(j)=='S') {
					x=i;
					y=j;}
				maze[i][j]=s.charAt(j);
			}
		}
		
		dfs(x,y);
		System.out.println(count);

	}
}

在内层这里时错误的 将注释部分打开在自己电脑跑一下就知道了
错误标记

马的覆盖点

思路:带限制深搜步数的所有可行解

/*
 蒜头君迷上了中国象棋,在和一个大师的巅峰对决中处于下风.他知道自己再走三步大师就会赢下这一局。于是蒜头君想背水一战。他想知道这个马三步可以到达的位置,是否有好的对策可以给大师致命一击。直接想出马三步能到达的所有位置,现在的小信已经大脑不能够用了。于是他找到他的好朋友你来帮忙解决这个问题。马走动的方法是一直一斜,即先横着或直着走一格,然后再斜着走一个对角线,俗称“马走日”。马一次可走的选择点可以达到四周的八个点,故有“八面威风”之说。如果在要去的方向有别的棋子挡住,马就无法走过去,俗称“蹩马腿”(当然这里就没有蹩马腿了)。
输入:
第一行输入两个整数 n (1 ≤ x ≤ 100), m(1≤ m ≤ 100) 代表棋盘行和列的大小。
第二行输入两个整数 x (1 ≤ x ≤ n), y (1 ≤ y ≤ m) 代表马开始所的位置。

输出:
输出整个棋盘,'.'代表棋盘上可以落子的点。'#'这个代表马三步能到达的点。。

测试数据:
10 9
10 1
答案:
.........
.........
.........
.#.#.....
#.#.#....
# # .#...
# # #.#..
# .# #...
#.# #.#..
# # # ...
 */
package 迷宫类可行解;
import java.util.Scanner;

public class 马的覆盖点 {
	static int n;
	static int m;
	static char[][]chess;
	static boolean[][] vis;
	private static void dfs(int x, int y,int step) {
		if(step>3||x<1||x>n||y<1||y>m) {//将深搜步数大于3 或者越界这些不合法数据返回 
			return;}
		step++;
		chess[x][y]='#';

		dfs(x-2,y-1,step);
		dfs(x-1,y-2,step);
		dfs(x+1,y-2,step);
		dfs(x+2,y-1,step);
		
		dfs(x+2,y+1,step);
		dfs(x+1,y+2,step);
		dfs(x-1,y+2,step);
		dfs(x-2,y+1,step);
		
		
	}


	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		int x,y;
		n=reader.nextInt();
		m=reader.nextInt();
		x=reader.nextInt();
		y=reader.nextInt();
		chess=new char[n+1][m+1];
		for (int i = 1; i <=n; i++) {
			for(int j=1;j<=m;j++) {
				chess[i][j]='.';
			}
		}
		dfs(x,y,0);
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=m;j++) {
				System.out.print(chess[i][j]);
			}
			System.out.println();
		}

	}


	
}

三、最优解(求迷宫最短步数)

迷宫最短路

/*问题描述
给一个n行m列的迷宫,'S'表示迷宫的起点,'T'表示迷宫的终点,
'*'表示不能通过的点,'.'表示可以通过的点。现在要求从'S'出发走到'T',
每次只能上下左右走动,并且只能进入能通过的点,每个点只能通过一次,
问是否能够到达终点,如果不可以输出"NO!",可以则输出最短步数。

输入:
第一行输入行数n和列数m
接下来n行输入n行m列的迷宫

输出:
如果不可以输出"NO!",可以则输出最短步数

测试数据:
5 6
....S*
.**...
.*..*.
*..**.
.T....

答案:7
 */
package 迷宫类最优解;

import java.util.Scanner;
public class 迷宫最短路 {
	private static int n;
	private static int m;
	private static int ans=1000000;
	private static boolean flag;
	static char[][] maze;
	static boolean[][] visited;
	static int[][] dire= {{-1,0},{0,1},{1,0},{0,-1}};
	
	private static boolean in(int x,int y) {
		return 0<=x&x<n&0<=y&y<m;
	}

	static void dfs(int x,int y,int step) {
		System.out.println(x+" "+y+" "+step);
		if(step>=ans) {//最优性剪枝
			return;
		}
		if(maze[x][y]=='T') {
			if(step<ans)
				ans=step;
			flag=true;
			System.out.println(ans);
			return;//到了终点return 返回最近调用的那一层
		}
		int dx,dy;
		visited[x][y]=true;
		for(int i=0;i<4;i++) {
			dx=x+dire[i][0];
			dy=y+dire[i][1];
			if(in(dx,dy)&&maze[dx][dy]!='*'&&!visited[dx][dy]) {
				System.out.println("before:"+dx+" "+dy+" step="+step);
				dfs(dx,dy,step+1);//不为step++(调用结束才++)  而为++step(或step+1)
				//回溯 四个方向都不行的话退点
				System.out.println(dx+" "+dy+" step="+step);
				//System.out.println("----------------------");
				//return 返回上一层调用
				//此处如果写return只会按dire数组方向搜索返回一组可行解或者无解
			}
		}
		visited[x][y]=false;//找全部解 需取消标记 下次到达这点的路径步长可能不一样
	}
	private static void show(char[][] array) {
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				System.out.print(array[i][j]);
			}
			System.out.println();
		}
		
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		n=reader.nextInt();
		m=reader.nextInt();
		int x = 0,y = 0;//局部变量需初始化
		maze=new char[n][m];
		visited=new boolean[n][m];
		for (int i = 0; i <n; i++) {
			String s=reader.next();
			for (int j = 0; j < m; j++) {
				if(s.charAt(j)=='S') {
					x=i;
					y=j;}
				maze[i][j]=s.charAt(j);
			}
		}
		show(maze);
		dfs(x,y,0);
		if(flag) {
			System.out.println(ans);
		}
		else
			System.out.println("NO!");
	}



}

四、连通块数目

踏青

/*
 蒜头君和他的朋友周末相约去召唤师峡谷踏青。他们发现召唤师峡谷的地图是由一块一块格子组成的,有的格子上是草丛,有的是空地。草丛通过上下左右 4 个方向扩展其他草丛形成一片草地,任何一片草地中的格子都是草丛,并且所有格子之间都能通过上下左右连通。如果用'#'代表草丛,'.'代表空地,
下面的峡谷中有 2 片草地。
# ..
..# 
处在同一个草地的 2 个人可以相互看到,空地看不到草地里面的人。他们发现有一个朋友不见了,现在需要分头去找,每个人负责一片草地,蒜头君想知道他们至少需要多少人。
输入格式
第一行输入n, m(1≤n,m≤100) 表示峡谷大小
接下来输入 n 行字符串表示峡谷的地形
输出格式
输出至少需要多少人

测试数据

5 6
.#....
..#...
..#..#
...#..
.#....
结果:5
 */

思路:以每一块草丛为起点dfs向外拓展 同时将遍历到的草丛用visited标记 一次深搜下来便是以该草丛为中心的草地

package 连通块数目;
import java.util.Scanner;

public class 踏青 {
	private static int n;
	private static int m;
	static char[][] maze;
	static boolean[][] visited;
	static int[][]dir= {{-1,0},{0,-1},{1,0},{0,1}};
	
	static void dfs(int x,int y) {
		//System.out.println(x+" "+y);
		int dx,dy;
		visited[x][y]=true;
		for(int i=0;i<4;i++) {
			dx=x+dir[i][0];
			dy=y+dir[i][1];
			if(in(dx,dy)&&maze[dx][dy]=='#'&&!visited[dx][dy]) {
				dfs(dx,dy);
			}
		}
		//visited[x][y]=false; 	错误代码走过的又被取消标记	
	}
	private static boolean in(int x, int y) {
		// TODO Auto-generated method stub
		return x>=0&&x<n&&y>=0&&y<m;
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		n=reader.nextInt();
		m=reader.nextInt();
		int x = 0,y = 0,count=0;//局部变量需初始化
		maze=new char[n][m];
		visited=new boolean[n][m];
		for (int i = 0; i <n; i++) {
			String s=reader.next();
			for (int j = 0; j < m; j++) {
				maze[i][j]=s.charAt(j);
			}
		}
		for (int i = 0; i <n; i++) {
			for (int j = 0; j < m; j++) {
				if(maze[i][j]=='#'&&!visited[i][j]) {
						dfs(i,j);
						count++;
					}				
			}
		}
		System.out.println(count);		
	}

}

五、最大连通块

/*题目描述
这一天蒜头君生日,他的朋友们一起来给蒜头君买一个大的蛋糕过生日。游戏做完后到了切蛋糕的时刻了,朋友们知道蒜头君喜欢吃蛋糕,便让蒜头君自己给自己切一块最大的。蒜头君看朋友们这么热情也就不客气了。这块蛋糕是由 R×C 的网格构成,每个网格上面都放有不同的水果。蒜头君把这些水果分为两类,一类是自己喜欢吃的水果,用'#'来表示;一类是自己不喜欢吃的水果,用'.'来表示。蒜头君对切出的蛋糕有如下要求:
 1. 切出的蛋糕连成一块(可以不为矩形,但必须在网格上连通)
 2. 切出的蛋糕只包含自己喜欢吃的水果
请问,蒜头君最大可以吃到多大的蛋糕?

输入格式:
第一行输入两个被空格隔开的整数 R(1≤R≤1000) 和 C(1≤C≤1000)。
然后会有一个 R×C 的网格,由'#'和'.'组成。

输出格式
输出一个整数,表示蒜头君可以吃到的蛋糕最大是多少(即对应到网格中的格子数)。

测试数据:
 5 6
 .#....
 ..#...
 ..#..#
 ...# .
 .#....
 答案:2
 */

思路:求最大连通块

package 最大连通块;

import java.util.Scanner;

public class 最大蛋糕块 {
	static int n;
	static int m;
	static int ans;
	static int cnt;
	static char[][] maze;
	static boolean[][] visited ;
	static void dfs(int x,int y) {
		if(x<0||x>=n||y<0||y>=m||visited[x][y]||maze[x][y]=='.') {
			return;
		}
		//在dfs函数里面计数 每访问一个网格 蛋糕便多了一格
		cnt++;
		visited[x][y]=true;//不能取消标记 就是要把所有蛋糕都走完
		dfs(x-1,y);
		dfs(x,y-1);
		dfs(x+1,y);
		dfs(x,y+1);
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		n=reader.nextInt();
		m=reader.nextInt();
		maze=new char[n][m];
		visited=new boolean[n][m];
		for(int i=0;i<n;i++) {
			String s=reader.next();
			for(int j=0;j<m;j++) {
				maze[i][j]=s.charAt(j);
			}		
		}
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				if(maze[i][j]=='#'&&!visited[i][j]) {
					cnt=0;//每次求连通块 计数器首先要清零
					dfs(i,j);
					//如果新得到的蛋糕块更大
					if(cnt>ans)
						ans=cnt;
				}
			}		
		}
		System.out.println(ans);
	}
}

六 带备忘录的DFS

一般模板:

int dfs()
{
	if(越界) return 0;
	//调用前先查询
    if(备忘录有值) return 备忘录的解;
    //记录值
    int ans=0;
    if(满足条件)
        {
            if(到达目的){
            	ans++;
            	return ans;
            }
            return ans;
        }
    for(枚举路径总数){
        if(满足递归条件){
                int t=dfs(继续递归);
                找出最优值;
        }
      }
    return 最优值;
}

地宫取宝

【题目描述】
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

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
当走到一个格子状态相同,也就是x坐标,y坐标,手中的最大价值和手中拿取的物品的数量跟之前从其他路径过来是一样的时候。那么从这个格子出发继续往下递归就是重复求解了,而不同路径到达同一点的时候这个时候的两条路径进行一样的递归这样就造成了子问题的重复求解了,
所以为了避免这个问题我们需要声明一个四维的数组来进行记录,第一个维度是x横坐标,
第二个维度是y纵坐标,第三个维度是当前我手中拿取的最大价值,第四个维度是当前我手中拿取的物品的数量。由于递归调用是自顶向下,但是返回值时是自底向上的。因此这个数组存放的就是从当前位置到终点的方案数。

另外 需要注意的是
我们每次调用递归方法都需要将计数变量置为零,因为对于当前的格子x,y来说我当前的能够走的方法数为零,在往下走的时候满足条件才会进行累加,所以不能够使用一个全局变量来进行累加
否则每一次递归都造成了对全局变量的修改这样做是不对的。

package _2014;

import java.util.Scanner;
public class _t09地宫取宝_记忆dfs {
	static int n,m,k;
	static long ans;
	static int mod=1000000007;
	static int maze[][];
	static long memo[][][][];
	public static long dfs(int x,int y,int curMax,int cnt) {
		if(x>=n||y>=m||cnt>k)
			return 0;
		//调用前先查询
		if(memo[x][y][curMax+1][cnt]!=-1)
			return memo[x][y][curMax+1][cnt];
		int cur=maze[x][y];
		long ans=0;
		if(x==n-1&&y==m-1) {//到达出口且对出口处的宝物进行了捡与不捡决策
			if(cnt==k||(cnt==k-1&&cur>curMax)) {
				ans=(ans+1)%mod;
			}
			//没有则此时ans==0
			return ans;
		}
		
		if(cur>curMax) {
			ans+=dfs(x+1, y, cur, cnt+1);
			ans+=dfs(x, y+1, cur, cnt+1);
		}
		ans+=dfs(x+1, y, curMax, cnt);
		ans+=dfs(x, y+1, curMax, cnt);
		memo[x][y][curMax+1][cnt]=ans%mod;
		return memo[x][y][curMax+1][cnt];
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner reader=new Scanner(System.in);
		
		n=reader.nextInt();
		m=reader.nextInt();
		k=reader.nextInt();
		maze=new int[n][m];
		//Ci范围:[-1,12]
		memo=new long[50][50][14][13];
		for (int i = 0; i < 50; i++) {
			for (int j = 0; j < 50; j++) {
				for (int k = 0; k < 14; k++) {
					for (int m = 0; m < 13; m++) {
						memo[i][j][k][m]=-1;
					}
					
				}
				
			}
			
		}
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				maze[i][j]=reader.nextInt();
			}
		}
		
		System.out.println(dfs(0, 0, -1, 0));
	}

}

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值