直接DFS会超时,第一种改进是利用对称性减枝,http://blog.sina.com.cn/s/blog_4e4c6ca501000cvj.html
第二种改进是使用位运算,见matrix67大牛: http://www.matrix67.com/blog/archives/122
第一版代码,单纯DFS,没有利用对称剪枝,test8 超时;
/*
ID: wangxin12
PROG: checker
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>
#include <string>
using namespace std;
int num_Queens; // 6 <= N <= 13
int board[14][14] = { 0 }; // 0 空格, 1 放了皇后, 2 未放子但有危险不能再放子
int answer[3][14] = { 20 };
int result = 0;
bool Update(vector<int> & ans, int k);
void Place(int board[14][14], int col);
bool IsSafe(int board[14][14], int rowToTry, int col);
void init() {
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 14; j++) {
answer[i][j] = 999;
}
}
}
int main() {
ifstream fin("checker.in");
fin>>num_Queens;
fin.close();
init();
Place(board, 1);
//挑选最小的三个出来
ofstream fout("checker.out");
for(int i = 0; i < 3; i++) {
for(int j = 0; j < num_Queens; j++) {
if( j == num_Queens - 1) fout<<answer[i][j]<<endl;
else fout<<answer[i][j]<<" ";
}
}
fout<<result<<endl;
fout.close();
return 0;
}
bool Update(vector<int> & ans, int k) {
bool flag = true;
for(int i = 0; i < num_Queens; i++) {
if( ans[i] > answer[k][i]) {
flag = false;
break;
}
if(ans[i] < answer[k][i]) {
flag = true;
break;
}
}
if(flag) {
for(int j = 0; j < ans.size(); j++) {
int temp = answer[k][j];
answer[k][j] = ans[j];
ans[j] = temp;
}
return true;
}
return false;
}
void Place(int board[14][14], int col) {
if(col > num_Queens) {
vector<int> tempResult;
//把每个queen的位置记录下来
for(int i = 1; i <= num_Queens; i++) {
for(int c = 1; c <= num_Queens; c++) {
if(board[i][c]) {
//cols.push_back(c);
tempResult.push_back(c);
break;
}
}
}
//如果当前结果最小,填入answer数组中
bool updated = false;
updated = Update(tempResult, 0);
updated = Update(tempResult, 1);
updated = Update(tempResult, 2);
result += 1;
return; //base case
}
for(int rowToTry = 1; rowToTry <= num_Queens; rowToTry++) {
if (IsSafe(board, rowToTry, col)) {
board[rowToTry][col] = 1; // place queen here
Place(board, col + 1); // recur to place rest
board[rowToTry][col] = 0; // failed, remove, try again
}
}
}
bool IsSafe(int board[14][14], int row, int col) {
bool flag = true;
//检查格子所在行,列是否安全
for(int i = 1; i < num_Queens; i++) {
if(board[row][i]) return false; // row is clear
if(board[i][col]) return false; //(optional)col is clear
}
//查对角线上是否已有Queen,因为从棋盘左到右摆棋子,所以只用查左边的对角线是否安全,即西北,西南两方向
int r = 1, c = 1;
//northwest diagonal is clear
for(r = row, c = col; r >= 1 && c >= 1; r--, c--) {
if(board[r][c]) return false;
}
//southwest diagonal is clear
for(r = row, c = col; r <= num_Queens && c >= 0; r++, c--) {
if(board[r][c]) return false;
}
return true;
}
第二版代码,利用对称剪枝,对称的原理——
“根据八皇后的对称关系。如果n 为偶数,则只需对第一行搜一半,然后搜出来的数目再乘以2;如果为奇数,对第一行中点以前的搜索,然后搜出来的数目乘以2再加上中点搜出来的数目。这样会提高一倍的速度。但因为n = 6时,由于要输出前三种方案,这三种方案,恰好又一种必须经过第一行后半部分的搜索才搜得到。所以必须对其做点处理。”
“利用对称性和链表,代码极其长,不如位运算。对称性是指
100
010
001
001
010
100
可以通过推出一种情况后乘以2,搜索时限定第一行的皇后在前半部分,搜完乘以2,列是奇数的话再单独搜第一个皇后在正中间那一列(此时第2个皇后可以在前半部分,也是一种时间优化),再加上链表优化,时间变为原来一半。USACO数据能过”
我的代码,非常搓
/*
ID: wangxin12
PROG: checker
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>
#include <string>
using namespace std;
int num_Queens; // 6 <= N <= 13
int board[14][14] = { 0 }; // 0 空格, 1 放了皇后, 2 未放子但有危险不能再放子
int answer[3][14] = { 20 };
int result = 0;
bool Update(vector<int> & ans, int k);
void Place(int board[14][14], int col);
void Place6(int board[14][14], int col);
bool IsSafe(int board[14][14], int rowToTry, int col);
void init() {
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 14; j++) {
answer[i][j] = 999;
}
}
}
int main() {
ifstream fin("checker.in");
fin>>num_Queens;
fin.close();
init();
///////////////////////////////////////
if(num_Queens == 6) Place6(board, 1);
else Place(board, 1);
// result *= 2;
//////////////////////////////////////
//挑选最小的三个出来
ofstream fout("checker.out");
for(int i = 0; i < 3; i++) {
for(int j = 0; j < num_Queens; j++) {
if( j == num_Queens - 1) fout<<answer[i][j]<<endl;
else fout<<answer[i][j]<<" ";
}
}
fout<<result<<endl;
fout.close();
return 0;
}
bool Update(vector<int> & ans, int k) {
bool flag = true;
for(int i = 0; i < num_Queens; i++) {
if( ans[i] > answer[k][i]) {
flag = false;
break;
}
if(ans[i] < answer[k][i]) {
flag = true;
break;
}
}
if(flag) {
for(int j = 0; j < ans.size(); j++) {
int temp = answer[k][j];
answer[k][j] = ans[j];
ans[j] = temp;
}
return true;
}
return false;
}
void Place(int board[14][14], int col) {
if(col > num_Queens) {
vector<int> tempResult;
//把每个queen的位置记录下来
for(int i = 1; i <= num_Queens; i++) {
for(int c = 1; c <= num_Queens; c++) {
if(board[i][c]) {
//cols.push_back(c);
tempResult.push_back(c);
break;
}
}
}
//如果当前结果最小,填入answer数组中
Update(tempResult, 0);
Update(tempResult, 1);
Update(tempResult, 2);
if( (num_Queens % 2 != 0) && board[num_Queens / 2 + 1][1] == 1)
result += 1;
else result += 2;
return; //base case
}
///////////////////////////
/*第一列,row只能到 num_Queens 的一半*/
if(col == 1) {
int temp;
if(num_Queens % 2 == 0) temp = num_Queens / 2;
else temp = num_Queens / 2 + 1;
for(int rowToTry = 1; rowToTry <= temp; rowToTry++) {
if (IsSafe(board, rowToTry, col)) {
board[rowToTry][col] = 1; // place queen here
Place(board, col + 1); // recur to place rest
board[rowToTry][col] = 0; // failed, remove, try again
}
}
}
/*从第二列开始,可以铺满所有row*/
if(col > 1) {
for(int rowToTry = 1; rowToTry <= num_Queens; rowToTry++) {
if (IsSafe(board, rowToTry, col)) {
board[rowToTry][col] = 1; // place queen here
Place(board, col + 1); // recur to place rest
board[rowToTry][col] = 0; // failed, remove, try again
}
}
}
/**/
//////////////////////////
}
bool IsSafe(int board[14][14], int row, int col) {
bool flag = true;
//检查格子所在行,列是否安全
for(int i = 1; i < num_Queens; i++) {
if(board[row][i]) return false; // row is clear
// if(board[i][col]) return false; //(optional)col is clear
}
//查对角线上是否已有Queen,因为从棋盘左到右摆棋子,所以只用查左边的对角线是否安全,即西北,西南两方向
int r = 1, c = 1;
//northwest diagonal is clear
for(r = row, c = col; r >= 1 && c >= 1; r--, c--) {
if(board[r][c]) return false;
}
//southwest diagonal is clear
for(r = row, c = col; r <= num_Queens && c >= 0; r++, c--) {
if(board[r][c]) return false;
}
return true;
}
void Place6(int board[14][14], int col) {
if(col > num_Queens) {
vector<int> tempResult;
//把每个queen的位置记录下来
for(int i = 1; i <= num_Queens; i++) {
for(int c = 1; c <= num_Queens; c++) {
if(board[i][c]) {
//cols.push_back(c);
tempResult.push_back(c);
break;
}
}
}
//如果当前结果最小,填入answer数组中
bool updated = false;
updated = Update(tempResult, 0);
updated = Update(tempResult, 1);
updated = Update(tempResult, 2);
result += 1;
return; //base case
}
for(int rowToTry = 1; rowToTry <= num_Queens; rowToTry++) {
if (IsSafe(board, rowToTry, col)) {
board[rowToTry][col] = 1; // place queen here
Place6(board, col + 1); // recur to place rest
board[rowToTry][col] = 0; // failed, remove, try again
}
}
}