转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1303915357
大致题意:
八皇后的扩展:N皇后问题
解题思路:
先说说传统回溯的DFS思路:
按行逐行填放皇后,标记已填放的列,两斜边的标记则利用 斜率 进行标记,当 准备填放的点 与 任一已填放的点 之间的斜率为1或-1时,那么该位置不允许填放
虽说是八皇后的扩展,但这么一扩展就难很多了。。。
题目要求n值范围在 8到300之内,Time还限制在1000ms ,传统的DFS回溯肯定是用不了,用传统的方法求解,n值超过30就爆了
那么这题只能通过数学方法去解决,就是找出所有不同大小的棋盘中n个皇后的放置的一个规律,用数学的表达式或者序列去 构造 这个规律
一般成功构造到公式后,题目就很水了,这题用构造法复杂度最坏只有O(n),n最大还是只有300。。。。
但是难点就是如何推导出这个“规律”的公式了。。。
我个人觉得必须至少用手工画几十个不同size的棋盘才可能看出一点眉目。。。挑战性太强了,有兴趣的同学可以手动试试。。。
我做这题是盗用网上现成的公式(序列)的,说实话我很佩服找到这个规律的人,我觉得这个公式可以作为解决n皇后问题的专用公理了。。。
构造法公式(序列):
一、当n mod 6 != 2 或 n mod 6 != 3时:
[2,4,6,8,...,n],[1,3,5,7,...,n-1] (n为偶数)
[2,4,6,8,...,n-1],[1,3,5,7,...,n ] (n为奇数)
二、当n mod 6 == 2 或 n mod 6 == 3时
(当n为偶数,k=n/2;当n为奇数,k=(n-1)/2)
[k,k+2,k+4,...,n],[2,4,...,k-2],[k+3,k+5,...,n-1],[1,3,5,...,k+1] (k为偶数,n为偶数)
[k,k+2,k+4,...,n-1],[2,4,...,k-2],[k+3,k+5,...,n-2],[1,3,5,...,k+1],[n] (k为偶数,n为奇数)
[k,k+2,k+4,...,n-1],[1,3,5,...,k-2],[k+3,...,n],[2,4,...,k+1] (k为奇数,n为偶数)
[k,k+2,k+4,...,n-2],[1,3,5,...,k-2],[k+3,...,n-1],[2,4,...,k+1],[n ] (k为奇数,n为奇数)
(上面有六条序列。一行一个序列,中括号是我额外加上的,方便大家辨认子序列,子序列与子序列之间是连续关系,无视中括号就可以了。第i个数为ai,表示在第i行ai列放一个皇后;... 省略的序列中,相邻两数以2递增。)
1 /*代码一:构造法*/
2
3 //Memory Time
4 //188K 16MS
5
6 #include<iostream>
7 #include<cmath>
8 using namespace std;
9
10 int main(int i)
11 {
12 int n; //皇后数
13 while(cin>>n)
14 {
15 if(!n)
16 break;
17
18 if(n%6!=2 && n%6!=3)
19 {
20 if(n%2==0) //n为偶数
21 {
22 for(i=2;i<=n;i+=2)
23 cout<<i<<' ';
24 for(i=1;i<=n-1;i+=2)
25 cout<<i<<' ';
26 cout<<endl;
27 }
28 else //n为奇数
29 {
30 for(i=2;i<=n-1;i+=2)
31 cout<<i<<' ';
32 for(i=1;i<=n;i+=2)
33 cout<<i<<' ';
34 cout<<endl;
35 }
36 }
37 else if(n%6==2 || n%6==3)
38 {
39 if(n%2==0) //n为偶数
40 {
41 int k=n/2;
42 if(k%2==0) //k为偶数
43 {
44 for(i=k;i<=n;i+=2)
45 cout<<i<<' ';
46 for(i=2;i<=k-2;i+=2)
47 cout<<i<<' ';
48 for(i=k+3;i<=n-1;i+=2)
49 cout<<i<<' ';
50 for(i=1;i<=k+1;i+=2)
51 cout<<i<<' ';
52 cout<<endl;
53 }
54 else //k为奇数
55 {
56 for(i=k;i<=n-1;i+=2)
57 cout<<i<<' ';
58 for(i=1;i<=k-2;i+=2)
59 cout<<i<<' ';
60 for(i=k+3;i<=n;i+=2)
61 cout<<i<<' ';
62 for(i=2;i<=k+1;i+=2)
63 cout<<i<<' ';
64 cout<<endl;
65 }
66 }
67 else //n为奇数
68 {
69 int k=(n-1)/2;
70 if(k%2==0) //k为偶数
71 {
72 for(i=k;i<=n-1;i+=2)
73 cout<<i<<' ';
74 for(i=2;i<=k-2;i+=2)
75 cout<<i<<' ';
76 for(i=k+3;i<=n-2;i+=2)
77 cout<<i<<' ';
78 for(i=1;i<=k+1;i+=2)
79 cout<<i<<' ';
80 cout<<n<<endl;
81 }
82 else //k为奇数
83 {
84 for(i=k;i<=n-2;i+=2)
85 cout<<i<<' ';
86 for(i=1;i<=k-2;i+=2)
87 cout<<i<<' ';
88 for(i=k+3;i<=n-1;i+=2)
89 cout<<i<<' ';
90 for(i=2;i<=k+1;i+=2)
91 cout<<i<<' ';
92 cout<<n<<endl;
93 }
94 }
95 }
96 }
97 return 0;
98 }
===========华丽的分割线==========
1 /*传统DFS回溯 TLE (n超过30就难以搜索)*/
2
3
4 #include<iostream>
5 #include<cmath>
6 using namespace std;
7
8 typedef class
9 {
10 public:
11 int r,c;
12 }location;
13
14 location pos[301]; //标记已填放的皇后坐标
15
16 bool col[301]; //列标记
17 int n; //皇后数
18
19 bool DFS(int x,int p)
20 {
21 if(p==n)
22 return true;
23
24 bool sign;
25 for(int y=1;y<=n;y++) //枚举当前行x的每一列
26 {
27 bool flag=false;
28 for(int i=1;i<=p;i++) //枚举每一个已填放的皇后
29 if(abs(x-pos[i].r) == abs(y-pos[i].c)) //检查当前准备填放皇后的位置(x,y)是否在他们的斜边上
30 { //其实就是标记斜率,斜率等于1或-1的格不允许填放皇后
31 flag=true;
32 break;
33 }
34
35 if(!flag && !col[y])
36 {
37 col[y]=true;
38 pos[p+1].r=x;
39 pos[p+1].c=y;
40 sign=DFS(x+1,p+1);
41
42 if(sign)
43 return true;
44 else
45 col[y]=false; //回溯,pos[]的记录会自动被新值覆盖,所以不用特意处理
46 }
47 }
48 return false; //当前行x的所有列y均不允许放皇后,说明前一行填错了
49 }
50
51 int main(void)
52 {
53 while(cin>>n)
54 {
55 if(!n)
56 break;
57
58 int p=0; //pos[]指针,已填充的棋子数
59 memset(col,false,sizeof(col));
60
61 DFS(1,p);
62
63 for(int i=1;i<=n;i++)
64 cout<<pos[i].c<<' ';
65 cout<<endl;
66 }
67 return 0;
68 }