P1123 取数游戏(洛谷)
题目描述
一个N×M的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻8个格子中的一个即认为这两个数字相邻),求取出数字和最大是多少。
输入格式
第1行有一个正整数T,表示了有T组数据。
对于每一组数据,第一行有两个正整数N和M,表示了数字矩阵为N行M列。
接下来N行,每行M个非负整数,描述了这个数字矩阵。
输出格式
T行,每行一个非负整数,输出所求得的答案。
输入输出样例
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
输出 #1
271 172 99
解题思路:
直接用dfs是会TLE的(因为我直接用只跑通了4个测试样例),平常dfs中我们会使用for循环来改变要访问元素四个上下左右的位置,但是在此题中,dfs中套上for就会TLE。因此需要有一个更高效的移动方法:在此处,我们默认每次移动都是y+1;当时当y>m超过当前列数时,我们设置y=1(第一列)和x+1。由此看来,这个移动是固定的,而且我们可以知道下一步它的具体位置,在此方法中能确保所有位置都遍历到。移动的终止条件是x>n超过行数时return。
我们对于每一个位置都有取和不取值两种情况(这样就涵盖了所有情况组合),从第一个位置搜索:取和不取先各来一次dfs。在dfs中,到达某个元素时,他有两种状态:能取和不能取,对于能取的:则取一次,标记为1表示取过,在dfs; 然后回溯标记0,再不取其值直接dfs,对于不能取的(表示周围元素有被取过的): 直接dfs。
注意点:在这里不应该找到某个位置,取其值后就立即把它周围的元素给标记了,这样会带来很高的复杂度。dfs(x,y,sum)表示的是访问到xy位置时的状态,而且此时是否相加的情况也处理好了。因此下面代码的思路应该是访问到某个元素后,先找到它下一个要移动的位置,再去判断该位置是否能取其值,若能则接下来有两个dfs,若不能接下来有一个dfs。
代码:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 5 int n,m; 6 int a[10][10]={0}; 7 int map[10][10]={0}; 8 9 int maxx=0; //用来存放最大值 10 11 12 void dfs(int x,int y,int sum){ //x代表行,y代表列 sum当前值 13 14 maxx=max(maxx,sum); 15 16 //有规则的移动 列数加1直到列数最大时,此时列数变为1,行数++ 17 int my=y+1; 18 int mx=x; 19 if(my>m){ 20 my=1; 21 mx=x+1; 22 } 23 if(mx>n) return; //递归终止条件 24 25 26 if(!map[mx-1][my-1]&&!map[mx][my-1]&&!map[mx+1][my-1]&&!map[mx-1][my]&&!map[mx+1][my]&&!map[mx-1][my+1]&&!map[mx][my+1]&&!map[mx+1][my+1]){ 27 map[mx][my]=1; 28 dfs(mx,my,sum+a[mx][my]); 29 map[mx][my]=0; 30 } 31 32 dfs(mx,my,sum); 33 34 return; 35 } 36 int main(){ 37 int t; 38 cin>>t; 39 40 for(int i=1;i<=t;i++){ 41 n=m=0; 42 cin>>n>>m; 43 memset(a,0,sizeof(a)); 44 maxx=0; 45 46 for(int i=1;i<=n;i++){ 47 for(int j=1;j<=m;j++){ 48 cin>>a[i][j]; 49 } 50 } 51 52 memset(map,0,sizeof(map)); 53 dfs(1,1,0); //代表取当前第一个数 54 memset(map,0,sizeof(map)); 55 map[1][1]=1; 56 dfs(1,1,a[1][1]); //代表不取当前第一个数 57 cout<<maxx<<endl; 58 59 } 60 return 0; 61 }