取数游戏 - 暴力深搜

204 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

一个N×M的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻8个格子中的一个即认为这两个数字相邻),求取出数字和最大是多少。

来源:洛谷 www.luogu.com.cn/problem/P11…

输入格式

第1行有一个正整数T,表示了有T组数据。

对于每一组数据,第一行有两个正整数N和M,表示了数字矩阵为N行M列。

接下来N行,每行M个非负整数,描述了这个数字矩阵。

对于100%的数据,N,M≤6,T≤20

3
4 4
67 75 63 10
29 29 92 14
21 68 71 56
8 67 91 25
2 3
87 70 85
10 3 17
3 3
1 1 1
1 99 1
1 1 1

输出格式

T行,每行一个非负整数,输出所求得的答案。

271
172
99

二、思路分析:

根据题目意思,也就是说我们需要执行T组数据求结果,在求和的过程中,不能取相邻的数据。输入数据也很明显,我们只需要求出一组数据的结果输出一个答案即可。

1、 我们可以很明显的看出,输入数据是很多的,这个时候我们就可以用文件流优化一下输入

2、 我们的思路是从第0行第0列开始选择数,依次去判断( (0,0)(0,1)(0,2)..... ) 选中一个数,需要给它做一个标记,既是判断同一个位置的数不能加两次,也是判断一个数相邻的数有没有被加过。这个位置的数,可以加的时候,我们既可以选择加,也可以选择不加。

3、 一个位置相邻8个格子的位置我们可以使用一个for循环来得到,这里写在一个集合数字中,8个集合代表着8个方向。一个集合有两个数,分别是要加的行数和要加的列数( {-1,0} 就是行数-1,列数不变,也就是向上的方向)这里在进行8个格子位置的获取时,记得判断它的范围是否有效。

4、 这里因为要运行T次,所以我们要记得把我们需要的变量重置(例如数字矩阵的内容、加过的记录)

三、AC 代码:


import java.io.*;

public class eee {
	static int N , M , max  ;
        // 记录输入数据的数字矩阵 map
	static int [][] map ;
        // 判断是否加上了这个位置的数
	static boolean [][] visit;
        // 8个方向
	static int [][]zo = { {-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1} };
        // 全局变量输入流 in
	static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
	public static void main(String[] args) {
		int T = getInt();
           // T组数据的结果,依次输出
		for( int t = 0 ; t < T ; t++ ) {
			N = getInt();
			M = getInt();
                // 先把结果设置为一个最小的数,然后把每一次遍历的结果和它判断,并进行更改
			max = Integer.MIN_VALUE;
                // 因为T组数据的大小不一,我们需要重新设置
			map = new int[N][M];
			visit = new boolean[N][M];
			for( int i = 0 ; i < N ; i++ ) {
				for( int j = 0 ; j < M ; j++ ) {
                           // 这里有优化输入我是放在了一个函数里面,也可以使用其他方法,我比较习惯用这种
					map[i][j] = getInt();
				}
			}
			fun( 0 , 0 , 0 );
			System.out.println(max);
		}
	}
	private static void fun(int x, int y, int sum) {
           // 需要从下一行的0列开始判断了
		if( y == M ) {
			y = 0 ;
			x = x + 1 ;
		}
           // 没有行数了,结束遍历
		if( x == N ) {
			max = Math.max(max, sum);
			return;
		}
           // 遍历这个数位置(x,y) 旁边的8个格子,一旦有其中一个格子被选中过,那此时的(x,y)就不可以选,将flag变成1
		int flag = 0 ;
		for( int i = 0 ; i < 8 ; i++ ) {
			int xx = x + zo[i][0];
			int yy = y + zo[i][1];
			if( xx>=0 && xx<N && yy>=0 && yy<M && visit[xx][yy] == true ) {
				flag = 1;
				break;
			}
		}
                // 可以选的时候选
		if( flag == 0 ) {
			visit[x][y] = true ;
			fun( x , y+1 , sum + map[x][y] );
			visit[x][y] = false ;
		}
                // 可以选或者不可以选的时候不选
		fun( x, y+1, sum );
	}
	private static int getInt() {
		try {
			in.nextToken();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return (int) in.nval;
	}
}

四、总结:

这个就是一个暴力遍历,主要是要把思路想清楚,选中的任意两个数字不能相邻 => 这个数能不能选需要判断周围相邻的数是否被选

嗯,就酱~