算法竞赛入门经典:第七章 暴力求解法 7.11回溯法

/*
回溯法:
排列生成和子集枚举的两种方法:1递归,2遍历
遍历:优点:简单,缺点:增大枚举量,检验所有解
回溯法:含义 :递归时,将生成+检查过程结合
        适合:问题分成步骤,步骤采用不太多选择

回溯算法=递归枚举算法:分成若干步骤递归,若某一步无解,返回上一级调用,称为回溯。

八皇后问题:
在棋盘上放置8个皇后,使得她们互不攻击,此时每个皇后的攻击范围为同行同列和同对角线,要求找出所有解。
一个可行解
Q								
				Q				
							Q
					Q			
		Q						
						Q		
	Q							
			Q					

思路:
1 不知道剪枝条件是什么。
2 不知道怎样确认是否是8个
*/


/*
关键:
1 由于题目条件限制,每一行,每一列只有一个,将问题简化为:对每一行,选择不重复的列。
  剪枝条件:不在同一行和同一列,同一对角线。
2 排列数共8!=40320个
3 我们利用逐行放置来限定:行不同,通过列号不同来限定:列不同,主对角线:行号与列号相减值不同来限定主对角线不同,副对角线:行号与列号相加值不同限定
  副对角线不同。if(iArr[pos] == iArr[j] || pos - iArr[pos] == j - iArr[j] || pos + iArr[pos] == j + iArr[j])//剪枝条件
4 该题仍然抽象为:用递归来求不重复的全排列+剪枝条件
5 	if(pos == n)//递归出口是,位置已经到达顶点
	{
		cnt++;
	}
6 			if(isFind)//如果合法,继续递归
			{
				eightQueen(n,pos+1,iArr);
			}
7 八皇后方法2:
   for(int i = 0 ; i < n;i++)//这里的i表示列,pos表示行
8 if(!vis[0][i] && !vis[1][pos+i] && !vis[2][pos-i+n])//pos+i:表示行+列,pos-i+n:表示行-列+n,放置行减列可能为负,所以加n
9 				path[pos] = i;//列
				vis[0][i] = vis[1][pos+i] = vis[2][pos-i+n] = 1;//置访问标记已经访问过
				search(n,pos+1);
				vis[0][i] = vis[1][pos+i] = vis[2][pos-i+n] = 0;//回溯法,要重新置访问标记
				注意回溯法使用辅助的全局标记变量,记得要恢复原状
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXSIZE 1024
int cnt;
void eightQueen(int n,int pos,int* iArr)
{
	if(pos == n)//递归出口是,位置已经到达顶点
	{
		cnt++;
	}
	else
	{
		for(int i = 0 ; i < n ; i++)
		{
			iArr[pos] = i;//尝试把第pos行皇后放在第i列上
			bool isFind = true;
			for(int j = 0 ; j < pos ; j++)
			{
				if(iArr[pos] == iArr[j] || pos - iArr[pos] == j - iArr[j] || pos + iArr[pos] == j + iArr[j])//剪枝条件
				{
					isFind = false;
					break;
				}
			}
			if(isFind)//如果合法,继续递归
			{
				eightQueen(n,pos+1,iArr);
			}
		}
	}
}


//八皇后问题解法2:采用二维数组标记:列,主对角线,副对角线
int vis[2][MAXSIZE];//原来为0,表示没有访问过,一旦已经访问过置为1
int path[MAXSIZE];
int cnt1;

void search(int n,int pos)
{
	if(pos == n)//如果达到递归程序出口,直接退出
	{
		cnt1++;
	}
	else
	{
		for(int i = 0 ; i < n;i++)//这里的i表示列,pos表示行
		{
			//if(!vis[0][i] || )//如果为0表示还没有访问过,这是可以放置皇后
			if(!vis[0][i] && !vis[1][pos+i] && !vis[2][pos-i+n])//pos+i:表示行+列,pos-i+n:表示行-列+n,放置行减列可能为负,所以加n
			{
				path[pos] = i;//列
				vis[0][i] = vis[1][pos+i] = vis[2][pos-i+n] = 1;//置访问标记已经访问过
				search(n,pos+1);
				vis[0][i] = vis[1][pos+i] = vis[2][pos-i+n] = 0;//回溯法,要重新置访问标记
			}
		}
	}
}

int main(int argc,char* argv[])
{
	int n;
	while(EOF != scanf("%d",&n))
	{
		memset(vis,0,sizeof(vis));
		cnt = 0;
		cnt1 = 0;
		int pos = 0,pos1 = 0;
		int iArr[MAXSIZE];
		eightQueen(n,pos,iArr);
		search(n,pos1);
		printf("%d\n",cnt);
		printf("%d\n",cnt1);
	}
	system("pause");
	return 0;
}



你可能感兴趣的:(算法竞赛)