USACO Checker Challenge 位运算

本文探讨了解决N皇后问题的不同方法,包括直接DFS搜索及其优化版本。通过对称剪枝和位运算两种策略减少不必要的搜索,提高了求解效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接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  
        }  
    }  
  
}  





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值