转载请注明出处:優YoU http://blog.csdn.net/lyy289065406/article/details/6642789
大致题意:
八皇后的扩展:N皇后问题
PS: 我用了传统DFS回溯 和 构造法 两种方法去解决
解题思路:
先说说传统回溯的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递增。)
/*代码一:构造法*/ //Memory Time //188K 16MS #include<iostream> #include<cmath> using namespace std; int main(int i) { int n; //皇后数 while(cin>>n) { if(!n) break; if(n%6!=2 && n%6!=3) { if(n%2==0) //n为偶数 { for(i=2;i<=n;i+=2) cout<<i<<' '; for(i=1;i<=n-1;i+=2) cout<<i<<' '; cout<<endl; } else //n为奇数 { for(i=2;i<=n-1;i+=2) cout<<i<<' '; for(i=1;i<=n;i+=2) cout<<i<<' '; cout<<endl; } } else if(n%6==2 || n%6==3) { if(n%2==0) //n为偶数 { int k=n/2; if(k%2==0) //k为偶数 { for(i=k;i<=n;i+=2) cout<<i<<' '; for(i=2;i<=k-2;i+=2) cout<<i<<' '; for(i=k+3;i<=n-1;i+=2) cout<<i<<' '; for(i=1;i<=k+1;i+=2) cout<<i<<' '; cout<<endl; } else //k为奇数 { for(i=k;i<=n-1;i+=2) cout<<i<<' '; for(i=1;i<=k-2;i+=2) cout<<i<<' '; for(i=k+3;i<=n;i+=2) cout<<i<<' '; for(i=2;i<=k+1;i+=2) cout<<i<<' '; cout<<endl; } } else //n为奇数 { int k=(n-1)/2; if(k%2==0) //k为偶数 { for(i=k;i<=n-1;i+=2) cout<<i<<' '; for(i=2;i<=k-2;i+=2) cout<<i<<' '; for(i=k+3;i<=n-2;i+=2) cout<<i<<' '; for(i=1;i<=k+1;i+=2) cout<<i<<' '; cout<<n<<endl; } else //k为奇数 { for(i=k;i<=n-2;i+=2) cout<<i<<' '; for(i=1;i<=k-2;i+=2) cout<<i<<' '; for(i=k+3;i<=n-1;i+=2) cout<<i<<' '; for(i=2;i<=k+1;i+=2) cout<<i<<' '; cout<<n<<endl; } } } } return 0; }
===========华丽的分割线==========
/*传统DFS回溯 TLE (n超过30就难以搜索)*/ //传统DFS回溯 TLE (超过30就失败) #include<iostream> #include<cmath> using namespace std; typedef class { public: int r,c; }location; location pos[301]; //标记已填放的皇后坐标 bool col[301]; //列标记 int n; //皇后数 bool DFS(int x,int p) { if(p==n) return true; bool sign; for(int y=1;y<=n;y++) //枚举当前行x的每一列 { bool flag=false; for(int i=1;i<=p;i++) //枚举每一个已填放的皇后 if(abs(x-pos[i].r) == abs(y-pos[i].c)) //检查当前准备填放皇后的位置(x,y)是否在他们的斜边上 { //其实就是标记斜率,斜率等于1或-1的格不允许填放皇后 flag=true; break; } if(!flag && !col[y]) { col[y]=true; pos[p+1].r=x; pos[p+1].c=y; sign=DFS(x+1,p+1); if(sign) return true; else col[y]=false; //回溯,pos[]的记录会自动被新值覆盖,所以不用特意处理 } } return false; //当前行x的所有列y均不允许放皇后,说明前一行填错了 } int main(void) { while(cin>>n) { if(!n) break; int p=0; //pos[]指针,已填充的棋子数 memset(col,false,sizeof(col)); DFS(1,p); for(int i=1;i<=n;i++) cout<<pos[i].c<<' '; cout<<endl; } return 0; }