Deep first search Luogu P1123 retrieval game problem solving ideas

Posted by zingbats on Mon, 03 Jan 2022 00:06:18 +0100

I sprout a new one. If there is something wrong, please leave a message in the comment area.

(。・∀・)ノ゙

subject

An N × For the number matrix composed of non negative integers of M, you need to take out several numbers so that any two numbers taken out are not adjacent (if a number is adjacent to another number in one of the 88 grids, it is considered that the two numbers are adjacent), and find out the maximum sum of the numbers taken out.

Input format
The first line has a positive integer TT, indicating that there is TT group data.

For each set of data, the first row has two positive integers NN and MM, indicating that the digital matrix is NN row and MM column.

Next, NN rows, MM nonnegative integers per row, describe this numeric matrix.

Output format
TT line, each line is a non negative integer, and the obtained answer is output.

Sample
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

Sample results
271
172
99

Problem solution

DFS
Each dfs() selects a number. When there is no optional number, compare the current result pMax with the final result Max and update max

Some questions:
1. Selecting position 0 and then position 3 has the same effect as selecting position 3 and then position 0. How to avoid unnecessary calculation?
First select the low sign and then the high sign, which can also be regarded as a pruning.
Implementation: after the parent node selects a number, it passes the position i behind the number to the child node parameter. The child node starts from i and cannot be selected forward
2. How does the parent node transfer information (which locations are not optional) to the child node?
Introduce the global array pos [] to record whether each position is optional. 1 means optional and 0 means not optional.
After the parent function selects a number, it changes pos [] and then calls the child function. It should be noted that since I use the global variable (pos), after returning from the child function, I have to back out the parent function's operation on pos this time. The advantage of using the global variable is to reduce the time spent in parameter transmission during recursion.

AC code

#include <iostream>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;

int T;
int N, M;
int D[6+1][6+1];//Used to store each set of data 
int Max, pMax;//Max is the result of the current group, and pMax saves the intermediate result of each path in the current group of DFS 
int pos[6*6+5];//It is used to record position information. pos[i] = 1 indicates the position number starting from two-dimensional array D. can I (number starting from 0) be selected at present 


void dfs(int k){
	if(k >= N*M){
		Max = max(Max, pMax);
		return;
	}
	int flag = 0;
	for(int i=k; i<N*M; i++){
		if(pos[i]){
			//Select the ith position
			flag = 1;
			int row, cow;
			row = i/M;
			cow = i-M*row;
			pMax +=  D[row][cow];
			//The lower part is set to the right of i, the lower left, the lower right, and the lower right is 0
			int mem[4] = {0};
			int old[4] = {0};
			if(i+1<N*M && (i+1)/M == row){
				//right 
				old[0] = pos[i+1];
				pos[i+1] = 0;
				mem[0] = 1;
			}
			if(i+M-1<N*M && (i+M-1)/M == row+1){
				//lower left
				old[1] = pos[i+M-1];
				pos[i+M-1] = 0;
				mem[1] = 1;
			}
			if(i+M < N*M){
				//Below 
				old[2] = pos[i+M];
				pos[i+M] = 0;
				mem[2] = 1;
			}
			if(i+M+1<N*M && (i+M+1)/M == row+1){
				//lower right
				old[3] = pos[i+M+1];
				pos[i+M+1] = 0;
				mem[3] = 1;
			}
			dfs(i+1);
			//Let's roll back pMax and pos 
			pMax -= D[row][cow];
			if(mem[0]){
				pos[i+1] = old[0];
			}
			if(mem[1]){
				pos[i+M-1] = old[1];
			}
			if(mem[2]){
				pos[i+M] = old[2];
			}
			if(mem[3]){
				pos[i+M+1] = old[3];
			}
		}
	}
	if(!flag){
		//There are no more numbers to choose from. Compare the results directly 
		Max = max(Max, pMax);
	}
}

void init(){
	//Initialize some information 
	Max = pMax = 0;
	for(int i=0; i<N*M; i++){
		pos[i] = 1;
	}
}

void gmn(){
	init();
	dfs(0);
}

int main(int argc, char *argv[]) {
	cin>>T;
	for(int i=0; i<T; i++){
		cin>>N>>M;
		for(int i=0; i<N; i++){
			for(int j=0; j<M; j++){
				cin>>D[i][j];
			}
		}
		gmn();
		cout<<Max<<endl;
	}
	return 0;
}

improvement

Because the data range of this problem is relatively small, the requirements for recursion times are not very high. In fact, pruning can be carried out. For example, after selecting several data, there may be a gap between these numbers, such as:
Data:
23 21 34 87 22
24 34 33 98 35
33 88 98 78 67
32 78 19 23 76
If you select 23, 22, 33, 98 and 76 in turn, then 34 in the third column of the first row is an optional number, but there is no selected number. Since the problem is the maximum sum, the result of this method must not be the maximum.
My improvement idea: check whether there is a gap regularly. If there is a gap, prune directly. Since each line affects the previous line at most, that is, each line can fill the gap of the previous line at most and cannot fill the previous one, you can detect the previous line of the previous line of i every time you call dfs(i). If a gap is found, prune.
Since I only used pos=0 for the position behind the number before fetching, when using this method, I should use pos=0 for the positions of all numbers in the Jiugong lattice where the number is located

Improved code

#include <iostream>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;

int T;
int N, M;
int D[6+1][6+1];//Used to store each set of data 
int Max, pMax;//Max is the result of the current group, and pMax saves the intermediate result of each path in the current group of DFS 
int pos[6*6+5];//It is used to record position information. pos[i] = 1 indicates the position number starting from two-dimensional array D. can I (number starting from 0) be selected at present
//int Counts;

void dfs(int k){
	//Counts++;
	if(k >= N*M){
		Max = max(Max, pMax);
		return;
	}
	int r = k/M;
	if(r-2>=0){
		r -= 2;
		for(int i=0; i<M; i++){
			if(pos[r*M+i]){
				return;
			}
		}
	}
	int flag = 0;
	for(int i=k; i<N*M; i++){
		if(pos[i]){
			//Select the ith position
			flag = 1;
			int row, cow;
			row = i/M;
			cow = i-M*row;
			pMax +=  D[row][cow];
			int mem[8] = {0};
			int old[8] = {0};
			if(i+1<N*M && (i+1)/M == row){
				//right 
				old[0] = pos[i+1];
				pos[i+1] = 0;
				mem[0] = 1;
			}
			if(i+M-1<N*M && (i+M-1)/M == row+1){
				//lower left
				old[1] = pos[i+M-1];
				pos[i+M-1] = 0;
				mem[1] = 1;
			}
			if(i+M < N*M){
				//Below 
				old[2] = pos[i+M];
				pos[i+M] = 0;
				mem[2] = 1;
			}
			if(i+M+1<N*M && (i+M+1)/M == row+1){
				//lower right
				old[3] = pos[i+M+1];
				pos[i+M+1] = 0;
				mem[3] = 1;
			}
			pos[i] = 0;//itself 
			if(i-1 >= 0 && (i-1)/M == row){
				//left 
				old[4] = pos[i-1];
				pos[i-1] = 0;
				mem[4] = 1;
			}
			if(i-M-1 >= 0 && (i-M-1)/M == row-1){
				//upper left
				old[5] = pos[i-M-1];
				pos[i-M-1] = 0;
				mem[5] = 1; 
			}
			if(i-M >= 0){
				//upper
				old[6] = pos[i-M];
				pos[i-M] = 0;
				mem[6] = 1; 
			}
			if(i-M+1 >= 0 && (i-M+1)/M == row-1){
				//upper right 
				old[7] = pos[i-M+1];
				pos[i-M+1] = 0;
				mem[7] = 1;
			}
			dfs(i+1);
			//Let's roll back pMax and pos 
			pMax -= D[row][cow];
			if(mem[0]){
				pos[i+1] = old[0];
			}
			if(mem[1]){
				pos[i+M-1] = old[1];
			}
			if(mem[2]){
				pos[i+M] = old[2];
			}
			if(mem[3]){
				pos[i+M+1] = old[3];
			}
			pos[i] = 1;
			if(mem[4]){
				pos[i-1] = old[4];
			}
			if(mem[5]){
				pos[i-M-1] = old[5];
			}
			if(mem[6]){
				pos[i-M] = old[6];
			}
			if(mem[7]){
				pos[i-M+1] = old[7];
			}
		}
	}
	if(!flag){
		//There are no more numbers to choose from. Compare the results directly 
		Max = max(Max, pMax);
	}
}

void init(){
	//Initialize some information 
	Max = pMax = 0;
	for(int i=0; i<N*M; i++){
		pos[i] = 1;
	}
}

void gmn(){
	init();
	dfs(0);
}

int main(int argc, char *argv[]) {
	cin>>T;
	for(int i=0; i<T; i++){
		cin>>N>>M;
		for(int i=0; i<N; i++){
			for(int j=0; j<M; j++){
				cin>>D[i][j];
			}
		}
		gmn();
		cout<<Max<<endl;
	}
	//cout<<Counts<<endl;
	return 0;
}

The test case needs to call dfs()360 times before improvement, but only 276 times after improvement. In fact, there are not many improvements (lll ¬ ω ¬, what better way do you have to communicate in the comment area? Ben Mengxin is happy to reply ̀ ㅂ• ́)و ✧

summarize experience

When doing a question, there was an error in the place to go back at the beginning. I got stuck for a long time and was caught by the method of this global variable. When going back, if the current function is set to pos[i]=0, I will go back to pos[i] = 1. In fact, pos[i] may be 0 at the beginning, and then go back to 1 after setting pos[i]=0. It should be 0
This also taught me A lesson o(╥﹏╥) O: if it is in the form of A=B, you must carefully consider whether A was B before

Topics: Algorithm