USACO 1.5.4 Checker Challenge 题解
【算法】DFS 【难度】★★★★☆
思路:经典的N皇后问题,但是对于6 <= N <= 13的范围朴素搜索1s内无法出结果。所以要考虑加速。考虑到剪枝的优化空间有限,所以本题才用位运算+DFS。
事实上,这个思路来自于Matrix67的文章,按照我自己的理解描述一下:
同样是一个DFS的递归,带有三个参数row,lc,rc分别标记这一横行上受到纵列(|)、右倾对角线(\)和左倾对角线(/)上已经放过的棋子的影响有冲突的位置。标记方法是按位表示:用0表示无冲突,1表示有冲突
例如,如果一个6皇后的某次的lc二进制下为100101,这表示在第1、4、6个位置受到右倾对角线上已放过棋子的限制,在这一行上不能放。
由于每一横行上只能放一个,所以每次过程中将row,lc,rc中的所有禁位算出来并找出可以放棋子的位置,并递归。递归前把选中放棋子的位置对应的row,lc,rc中的相应位变为1。
当递归到某次时发现row的所有位都为1,证明所有的列都放置了棋子,则找到一个解。
利用这种方法速度非常的快:
最大的数据(n==13)时也只要0.227s,这是直接DFS比不了的。
PS. Matrix67关于位运算有一系列文章都写得很好,相当经典
http://www.matrix67.com/blog/archives/122个人觉得他的博客很不错,很多讲解都清晰透彻。
View Code
1 /*
2 ID: wsc5001
3 LANG: C
4 TASK: checker
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <math.h>
9 int n;
10 int aone=0,amore=0;//aone:二进制最大值
11 int ansct=0;
12 int saveans[15]={0},si=0;
13 int finans[73715][15],fi=0;
14 void printans()
15 {
16 int i;
17 for (i=0;i<n;i++)
18 finans[fi][i]=saveans[i];
19 fi++;
20 }
21 void findfs(int lc,int rc,int row)//lc 左对角线 rc 右对角线 row 纵列 1=不可放 0=可放
22 {
23 int i,togt;
24 if (row!=aone)
25 {
26 togt=lc|rc|row;
27 for (i=0;i<n;i++)
28 {
29 if ((togt&1)==0)
30 {
31 {saveans[si]=n-i;si++;}
32 findfs((lc+(1<<i))<<1,(rc+(1<<i))>>1,row+(1<<i));
33 {saveans[si]=0;si--;}
34 }
35 togt=togt>>1;
36 }
37 }
38 else
39 {printans();ansct++;return;}
40 }
41 int main()
42 {
43 FILE *fin,*fout;
44 fin=fopen("checker.in","r");
45 fout=fopen("checker.out","w");
46 fscanf(fin,"%d",&n);
47 int i,j,k;
48 int av=0;
49 aone=(1<<n)-1;
50
51 findfs(0,0,0);
52 //printf("%d",si);
53 for (i=fi-1;i>=fi-3;i--)
54 {
55 for (j=0;j<n-1;j++)
56 fprintf(fout,"%d ",finans[i][j]);
57 fprintf(fout,"%d",finans[i][j]);
58 fprintf(fout,"\n");
59 }
60 //printf("\n");
61 fprintf(fout,"%d\n",ansct);
62
63 //system("pause");
64 fclose(fin);
65 fclose(fout);
66 }
wsc500原创,转载请注明出处。请注明 出自http://www.cnblogs.com/loveidea/