洛谷OJ-P1219 八皇后-深度优先搜索

问题描述:
n皇后问题是高斯提出的一个经典的问题,是指在一个n*n的棋盘上放置n各皇后,每行一个并使其不能相互攻击(同一行、同一列、同一斜行上的两个皇后都会相互攻击),编写程序找出所有的放置方案。
例如,在下面的8×8棋盘格局中,若一个皇后放在第4行、第4列,则图(a)中所有标*的位置都不能再放置其他皇后,图(b)是一种可能的8皇后解决方案。

AC代码:

int n,sum,k=3,f[20];//n代表棋盘是n*n的,sum为可行解的个数,洛谷中要求输出前三组可行解,f[m]代表第m行的皇后放置在了第f[m]列,注意**代码中行与列均从1下标开始**
void dfs(int cur)//cur为current的缩写,意为**当前的**状态
{
    int i,j,flag;//flag用来标记能(1)否(0)在当前位置放置皇后
    if(cur==n+1)//cur等于n+1代表前1到n行已经被放置好了皇后,所以此时应该对sum累加并输出一组可行解然后返回
    {
        sum++;
        if(k)
        {
            for(i=1;i<=n;i++)
                printf("%d ",f[i]);
            printf("\n");
            k--;
        }
        return;
    }
    for(i=1;i<=n;i++)//需要在第1到n列上放置皇后,for循环来遍历所有的可能性
    {
        flag=1;//每一次循环都需要对flag进行赋值
        f[cur]=i;//将皇后放置于第cur行第i列,只是暂时放在这儿,至于行或不幸将在下面的for循环中进行判断
        for(j=1;j<=cur-1;j++)//此处将在下面的解决方法中详细说明
            if(f[cur]==f[j]||cur-f[cur]==j-f[j]||cur+f[cur]==j+f[j])
            {
                flag=0;
                break;
            }
        if(flag)//如果flag==1,代表皇后放置成功,那么就对下一行进行操作,即状态从对第cur行进行操作转移到对第cur+1行进行操作
            dfs(cur+1);
    }
}
int main()
{
    scanf("%d",&n);
    if(n!=13)
    {
        dfs(1);//初始状态
        printf("%d\n",sum);
    }
    else//由于当n==13时TLE了,于是就将答案打表来骗分
    {
        printf("1 3 5 2 9 12 10 13 4 6 8 11 7\n");
        printf("1 3 5 7 9 11 13 2 4 6 8 10 12\n");
        printf("1 3 5 7 12 10 13 6 4 2 8 11 9\n");
        printf("73712\n");
    }
    return 0;
}

解决方法:
这道题的代码来自刘汝佳的《算法竞赛入门指南(第2版)》,所用的方法是朴素的深度优先搜索,也叫做回溯法。整道题写了注释的部分都不难理解,我认为这道题目的难点主要在于对能否在点(cur,i)放置皇后的判断上:
①因为需要遍历棋盘的1到n列,所以再写一个for循环,以j做循环变量。
②因为我们对行进行操作,所以我们一定不会在同一行上放置两个皇后,因此不从行的角度考虑
③考虑皇后的列攻击的话,我们只需要判断是否一列上将会有两个皇后,也就是说我们要放置的点(cur,i)所对应的列i上是否已经被放置了一个皇后,由此转化为f[cur](=i)是否与f[j(j从1到cur-1,为之前放置过皇后的行号)](f[j]则代表第j行上的皇后放置在了第f[j]列)相等
④考虑皇后的主对角线攻击和副对角线攻击是同理的。这里需要推出元素在同一主对角线(或副对角线)所满足的条件,通过简单的计算,我们发现,位于同一主对角线上的元素行号减列号相同,位于同一副对角线上的元素行号加列号相同。那么判断条件就转化为判断点(cur,i)的行号减列号(cur-f[cur])是否与点(j,f[j])的行号减列号(j-f[j])相同了,即两点是否在同一条主对角线上。副对角线同理。
注:
①规范地画出棋盘,并表明行号、列号,将有助于对本题的求解和对题解的理解。
②找出数学规律将大大简化判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值