回溯法之----N皇后问题,全排列问题,求子集问题

N皇后问题

N皇后问题是八皇后问题的升级版主要思路也是通过回溯法来实现的
这列通过对角线判断进行了适当的优化
首先先贴上代码

#include <iostream>
using namespace std;
int cnt;
int a[12];
int col[12];
int dig1[24];
int dig2[24];
void placeQueen(int k,int &n)
{
    if(k==n+1)
    {
        cnt++;
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            a[k]=i;
            if(!col[i]&&!dig1[k+i-1]&&!dig2[k-i+n])
            {
                col[i]=dig1[k+i-1]=dig2[k-i+n]=1; //表示确定该位置后,对应列以及对角线的值改为1表示该位置对应的列和对角线已经被占用
                placeQueen(k+1,n);
                col[i]=dig1[k+i-1]=dig2[k-i+n]=0; //若递归回到这一步表示之后某一步出现了问题无法继续放置,则将之前这一步上的皇后取消 将对应的位置表达归0
            }
        }
    }
}
int main()
{
    int n;
    int save[11];
    memset(save,-1,sizeof(save));
    while(scanf_s("%d",&n)&&n!=0)
    {
        cnt=0;
        if(save[n]!=-1)   //表示之前若已经算出该个数的皇后直接将值输出
        {cout<<save[n]<<endl;}
        else
        {
        cnt=0;
        placeQueen(1,n);
        save[n]=cnt;
        cout<<cnt<<endl;
        }
    }
}

此处采取了对角线优化,dig1和dig2表示了每个皇后位置所对应的对角线,而col表示每个皇后所在列数
找出两条对角线之间的联系 如下图所示 ,
在这里插入图片描述
上图所示可以看出左斜对角线取值范围是[-3,3]而右斜对角线范围时[2,8] 这时候我们将左斜每个值同时加上4(即该数组的规模),并将右斜每个值-1,这时候左斜取值范围为[1,7]而右斜取值范围也为[1,7]这时候我们就可以用一个大小为8的数组存放这些位置的状态。
dig1[k+i-1]和dig2[k-i+n]中的k+i-1和k-i+n就是这么来的,n表示数组行/列的规模,这里应该不难理解,这样每次若该皇后放置则对应的col和dig1,dig2的值就变为1,若撤销放置就改为0,一开始定义全局变量默认为0(即没有放置的情况)
以下代码就表示对应操作,这样上面的代码就很好理解了,通过对列以及两条对角线标记可以缩短一部分迭代所重复花费的时间

 col[i]=dig1[k+i-1]=dig2[k-i+n]=1;
 placeQueen(k+1,n);
 col[i]=dig1[k+i-1]=dig2[k-i+n]=0;

全排列问题

全排列问题为:
输入3
输出
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
可以看出这也可以通过回溯法来做,并且在这里我是直接通过修改N皇后的问题来解决这道题的,因为其实我们可以可以将三个数也看作是一个3x3棋盘的样子这里只需要注意该列不能重复放数即可

在这里插入图片描述
这里只需要写一个check函数判断是否在同一列即可 可以在原先的N皇后问题上进行一定的修改
代码如下

#include <iostream>
using namespace std;
int a[4];
int n=4;
bool check(int k)
{
	for(int i=1;i<k;i++)
	{
		if(a[i]==a[k]) //只要不在同一列即可
		return false;
	}
	return true;
}
void allsort(int k)
{
	if(k==n)
	{
		for(int i=1;i<k;i++)
		cout<<a[i]<<" ";
		cout<<endl;
	}
	else
		for(int i=1;i<n;i++)
		{
		    a[k]=i;
			if(check(k)) //如果通过check返回了true即可进入下一个位置
			allsort(k+1);
		}
}
int main()
{
	allsort(1);
}

结果为
在这里插入图片描述

子集问题

给出一个集合 求出其子集
集合:{1,2,3}
子集:{ },{1},{2},{3},{1 2},{1 3},{2 3},{1 2 3}
在这里我们可以发现这道题也可以转换为n皇后的问题,只需要使用所谓二进制的思想
这里 可以把每个子集看成是01的组合形式
因为有三个数
{ } 则按照1 2 3排列顺序可以看成 0 0 0
{1} 可以看成 1 0 0
{2} 可以看成 0 1 0
以此类推
其实就是三个数每个数数值为0 1的全排列问题 也可以使用n皇后思想
0 0 0 —> {}
0 0 1 —> {3}
0 1 0 —> {2}
0 1 1 —> {2 3}
1 0 0 —> {1}
1 0 1 —> {1 3}
1 1 0 —> {1 2}
1 1 1 —> {1 2 3}
八个二进制的组合对应的就是八种子集的情况
对应的代码如下,思路都和之前一样,只不过空子集需要加一个判断条件 这我们将空集设为null

#include <iostream>
using namespace std;

void allsort(int k,string &a,int & n,int* &count)
{
	if(k==n)
	{
		int x=0; //用于计数,判断空集的情况
		for(int i=0;i<k;i++)
		{
        if(count[i]==1)
		{
			cout<<a[i]<<" ";
			x++;
		}
		}
		if (x==0)
		 cout<<"null";
		 cout<<endl;
	}
	else
		for(int i=1;i>=0;i--)
		{
			count[k]=i;  //和之前的思想一样,只不过这里只有0 1 两种情况  这里每列都可以相同 
			allsort(k+1,a,n,count);
		}
}
int main()
{
	string a="12345";
	int n=a.size();
	int *count =new int[n];
	allsort(0,a,n,count);
}

结果
在这里插入图片描述
不过这里不是按照 {} {1} {2} {3} {1 2} {1 3} {2 3} {1 2 3} 来输出的 因为这里是对二进制的全排列
如果想按照从一位到三位的输出在重新写一个整合的函数就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值