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;
}
}
四、总结:
这个就是一个暴力遍历,主要是要把思路想清楚,选中的任意两个数字不能相邻 => 这个数能不能选需要判断周围相邻的数是否被选
嗯,就酱~