Problem state:
==============================================================================================================
Examine the 6x6 checkerboard below and note that the six checkers are arranged on the board so that one and only one is placed in each row and each column, and there is never more than one in any diagonal. (Diagonals run from southeast to northwest and southwest to northeast and include all diagonals, not just the major two.)
Column 1 2 3 4 5 6 ------------------------- 1 | | O | | | | | ------------------------- 2 | | | | O | | | ------------------------- 3 | | | | | | O | ------------------------- 4 | O | | | | | | ------------------------- 5 | | | O | | | | ------------------------- 6 | | | | | O | | -------------------------
The solution shown above is described by the sequence 2 4 6 1 3 5, which gives the column positions of the checkers for each row from 1 to 6:
ROW | 1 | 2 | 3 | 4 | 5 | 6 |
COLUMN | 2 | 4 | 6 | 1 | 3 | 5 |
This is one solution to the checker challenge. Write a program that finds all unique solution sequences to the Checker Challenge (with ever growing values of N). Print the solutions using the column notation described above. Print the the first three solutions in numerical order, as if the checker positions form the digits of a large number, and then a line with the total number of solutions.
Special note: the larger values of N require your program to be especially efficient. Do not precalculate the value and print it (or even find a formula for it); that's cheating. Work on your program until it can solve the problem properly. If you insist on cheating, your login to the USACO training pages will be removed and you will be disqualified from all USACO competitions. YOU HAVE BEEN WARNED.
A single line that contains a single integer N (6 <= N <= 13) that is the dimension of the N x N checkerboard.
6
The first three lines show the first three solutions found, presented as N numbers with a single space between them. The fourth line shows the total number of solutions found.
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4
=============================================================================================================
这题一眼看过去就是曾经做过的八皇后问题,我当时毫不犹豫地就把当时做leetcode里的代码翻出来了。提交后悲剧了。。TLE了。我的dfs照道理已经是最优的了。网上去翻答案,找到一个大牛的代码,惊为天人啊。。。下面我就把我开始的代码和它的代码(经修改)一起贴出来。
我的代码:
1 /* 2 ID: yingzho1 3 LANG: C++ 4 TASK: checker 5 */ 6 #include <iostream> 7 #include <fstream> 8 #include <string> 9 #include <map> 10 #include <vector> 11 #include <set> 12 #include <algorithm> 13 #include <stdio.h> 14 #include <queue> 15 #include <cstring> 16 17 using namespace std; 18 19 int row[14]; 20 bool col[14]; 21 int N; 22 23 ifstream fin("checker.in"); 24 ofstream fout("checker.out"); 25 26 27 bool check(int dep, int pos) { 28 for (int i = 0; i < dep; i++) { 29 if (abs(dep-i) == abs(pos-row[i])) return true; 30 } 31 return false; 32 } 33 34 void dfs(int dep, int &count) { 35 if (dep == N) { 36 count++; 37 if (count <= 3) { 38 for (int i = 0; i < N-1; i++) fout << row[i]+1 << " "; 39 fout << row[N-1]+1 << endl; 40 } 41 return; 42 } 43 for (int i = 0; i < N; i++) { 44 if (!col[i] && !check(dep, i)) { 45 col[i] = true; 46 row[dep] = i; 47 dfs(dep+1, count); 48 col[i] = false; 49 } 50 } 51 } 52 53 int main() 54 { 55 56 fin >> N; 57 memset(col, false, sizeof(col)); 58 int count = 0; 59 dfs(0, count); 60 fout << count << endl; 61 62 return 0; 63 }
他的代码:
1 /* 2 ID: yingzho1 3 LANG: C++ 4 TASK: checker 5 */ 6 #include <iostream> 7 #include <fstream> 8 #include <string> 9 #include <map> 10 #include <vector> 11 #include <set> 12 #include <algorithm> 13 #include <stdio.h> 14 #include <queue> 15 #include <cstring> 16 17 using namespace std; 18 19 ifstream fin("checker.in"); 20 ofstream fout("checker.out"); 21 22 int col[14], leftf[14], rightf[14]; 23 int N, full; 24 int anscount = 0; 25 26 int getindex(int n) { 27 int ret = -1; 28 while (n > 0) { 29 ret++; 30 n >>= 1; 31 } 32 return ret; 33 } 34 35 void dfs(int dep) { 36 if (dep == N) { 37 if (anscount < 3) { 38 int index; 39 for (int i = 0; i < N-1; i++) fout << getindex(col[i] ^ col[i+1])+1 << " "; 40 fout << getindex(col[N-1] ^ full)+1 << endl; 41 } 42 anscount++; 43 return; 44 } 45 int allow = full ^ (col[dep] | leftf[dep] | rightf[dep]); 46 while (allow != 0) { 47 int pos = allow & (~allow + 1); 48 col[dep+1] = col[dep] | pos; 49 leftf[dep+1] = ((leftf[dep] | pos) << 1) & full; 50 rightf[dep+1] = ((rightf[dep]) | pos) >> 1; 51 dfs(dep+1); 52 allow ^= pos; 53 } 54 } 55 56 int main() 57 { 58 fin >> N; 59 full = (1 << N) - 1; 60 dfs(0); 61 fout << anscount << endl; 62 63 return 0; 64 }
位运算果然很强大,不过这种写法实在太难想到了
还有一种我刚开始的写法的剪枝方法也能AC,引用别人的一段话:
左右对称的剪枝。
对于n为偶数的时候,第一行只搜到n/2处。这样对称过去实际上就是所有第一行皇后在n/2之后的方案。所以最终结果为sum*2。
对于n为奇数的时候,第一行只搜到(n-1)/2处。这样对称过去实际上就是所有第一行皇后在(n-1)/2+1之后的方案。那如果第一行皇后放在第(n-1)/2+1处怎么办呢?其实我们只需要记录下最后一行皇后在(n-1)/2+1处的方案总数sum2,这样对称上去就是所有第一行皇后在(n-1)/2+1处的方案总数。所以最终结果为(sum+sum2)*2。
具体代码我就省略不写了