POJ3239-Solution to the n Queens Puzzle

 

转载请注明出处:優YoU    http://blog.csdn.net/lyy289065406/article/details/6642789

大致题意:

八皇后的扩展:N皇后问题

 PS: 我用了传统DFS回溯 和 构造法 两种方法去解决

解题思路:

先说说传统回溯的DFS思路:

按行逐行填放皇后,标记已填放的列,两斜边的标记则利用斜率进行标记,当准备填放的点与任一已填放的点之间的斜率为1-1时,那么该位置不允许填放

 

虽说是八皇后的扩展,但这么一扩展就难很多了。。。

题目要求n值范围在 8300之内,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,表示在第iai列放一个皇后;... 省略的序列中,相邻两数以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;
}


 

 

 

 

 

你可能感兴趣的:(c,Class,扩展)