现在的位置: 首页 > 综合 > 正文

POJ1321 搜索之回溯法

2013年02月15日 ⁄ 综合 ⁄ 共 1163字 ⁄ 字号 评论关闭

因为看《算法竞赛入门经典》看到了讲回溯这一章节,就拿了一个搜索题来练练手。这个题用回溯做还比较简单。

主要思路是依次遍历每一个格子,首先看是不是空(#),如果是且与之前的棋子不冲突的话就放一个棋子在这里,记录该棋子所在行号和列号。之后再去寻找下一个能放棋子的位置,直到所有棋子放完可行解数量即+1;遇到没有可放的情况则返回上一层调用(这就是“回溯”)。第一次提交是LTE,分析原因错误出现在我的思路上。我想的是把每个棋子看成不同的,这样求解出答案后再除以所有棋子全排列的情况。以题目给的测试用例说明就是:

4 4
...#
..#.
.#..
#...

在这种情况下答案是1,而不是24,也就是说棋子都是一样的。而我一开始是按照棋子不一样去做,得出结果是24后再除以4!,即24/4!=1。虽然结果正确但由于搜索了太多没用的情况所以超时了。其实要想不重复也很简单,只需要在寻找下一个棋子时让行号从上一个棋子的行号+1开始即可做到不重复。另外在回溯的时候要特别注意修改了的全局变量要及时复原!这是非常容易错的一点!!!!切记!!!!!

下面为代码:

#include<iostream>
using namespace std;
char board[8][8];
int visit[8][2];		//visit[i][0]记录第i个棋子的行号visit[i][1]记录列号
int count;
void search(int cur,int k,int n){
	if(cur==k) count++;
	else{
		for (int i=cur?visit[cur-1][0]+1:0;i<n;i++){//回溯时行号为上一个棋子的行号+1
			for (int j=0;j<n;j++){					//如果cur是0,则行号从0开始
				if (board[i][j]=='.') continue;
				int ok=1;
				for (int p=0;p<cur;p++){
					if (visit[p][0]==i||visit[p][1]==j){	//检查是否会与前cur个值发生冲突
						ok=0;
						break;
					}
				}
				if(ok==1){
					visit[cur][0]=i;
					visit[cur][1]=j;
					search(cur+1,k,n);
					visit[cur][0]=0;		//切记!!!!!!!
					visit[cur][1]=0;		//将修改过的全局变量修改回来!
				}

			}
		}
	}
}
int main(){
	int n,k;
	while(cin>>n>>k&&n!=-1){
		count=0;
		memset(board,' ',sizeof(board));
		memset(visit,0,sizeof(visit));
		for (int i=0;i<n;i++){
			cin>>board[i];
		}
		search(0,k,n);
		cout<<count<<endl;
	}
	return 0;
}

抱歉!评论已关闭.