1214:八皇后

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 × 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2…b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。
【输入】
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1≤b≤92)。
【输出】
输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。
【输入样例】
2
1
92
【输出样例】
15863724
84136275

这个题的例子是八皇后,求解其余皇后个数,把n值修改即可(N<=12)

通过对它攻击位置的研究,可以发现,它攻击了上、下、左、右、左上、左下、右上、右下八个方向;
int xx[8]={0,0,-1,1,-1,-1,1,1};
int yy[8]={1,-1,0,0,1,-1,1,-1};
我们要考虑几个问题:
第一、皇后放置后,可以攻击到那些地方(数组标记是否被攻击)
第二、除去攻击的地方和已经放置的地方,还剩下哪些地方可以放置(数组存储放置位置,即答案)
第三、怎样回溯,因为一定是一行一行的放置,当遇到某一行没有地方的时候,就一定是之前的某一个棋子放错了位置,就返回到之前,需要用一个数组来保存,上一步骤产生的答案

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <string>

using namespace std;

//左上角为(0,0)
int xx[8]={0,0,-1,1,-1,-1,1,1};
int yy[8]={1,-1,0,0,1,-1,1,-1};
int a=0,n=8;
int attack[20][20];//标记皇后的攻击位置
char queen[20][20];//储存皇后的放置的位置
int solve[99];

void copy(int a[20][20],int b[20][20],int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            b[i][j]=a[i][j];
        }
    }
}
void put(int x,int y,int n)
{
    attack[x][y]=1;
    for(int i=1;i<n;i++)//从皇后位置向1——n-1个距离
    {
         for(int j=0;j<8;j++)//八个方向
         {
             int r=x+i*xx[j];
             int l=y+i*yy[j];
             if(r>=0&&r<n&&l>=0&&l<n) attack[r][l]=1;
         }
    }
}
void backtracking(int n,int k)
{
     if(k==n)//终止条件
     {
         a++;
         for(int i=0;i<n;i++)
         {
             for(int j=0;j<n;j++)
             {
                 if(queen[i][j]=='Q') solve[a]=solve[a]*10+j+1;
             }
         }
         return ;
     }
     for(int i=0;i<n;i++)//循环列
     {
         if(attack[k][i]==0)
         {
             int temp[20][20];
             copy(attack,temp,n);
             put(k,i,n);//更新攻击位置
             queen[k][i]='Q';
             backtracking(n,k+1);
             copy(temp,attack,n);//恢复
             queen[k][i]='.';//恢复
         }
     }
}

int main()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            attack[i][j]=0;
            queen[i][j]='.';
        }
    }
    backtracking(n,0);
    int t,p;
    cin>>t;
    for(int i=0;i<t;i++)
    {
        cin>>p;
        cout<<solve[p]<<endl;
    }

	return 0;
}

补充新方法:
巧妙地想到对角线的表示方法,利用一次函数,直接写一维数组标记即可,并且 n 可以大于12

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
int a[100],b[100],c[100],d[100];//这里的a标记了行,当前这行的列数,即答案
int solve[100];
int total=0;
int n=8,t,p;

void backtracking(int k)
{
    if(k==n)
    {
        total++;
        for(int i=0;i<n;i++)
            solve[total]=solve[total]*10+a[i]+1;

        return;
    }
    for(int j=0;j<n;j++)//尝试可能的位置
    {
        if((!b[j])&&(!c[k+j])&&(!d[k-j+n]))//如果没有皇后占领,执行以下程序
        {
            a[k]=j;//标记k排是第j个
            b[j]=1;//纵列 占领
            c[k+j]=1;//
            d[k-j+n]=1;//
            backtracking(k+1);
            //回溯到上一步
            b[j]=0;
            c[k+j]=0;
            d[k-j+n]=0;
        }
    }
}
int main()
{
    cin>>t;
    backtracking(0);
    for(int i=0;i<t;i++)
    {
        cin>>p;
        cout<<solve[p]<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值