P1219 [USACO1.5]八皇后 Checker Challenge(DFS回溯)

本文介绍了一种寻找跳棋布局解的高效算法。通过使用三个数组分别记录列和两条对角线的状态,避免了重复检查,显著提高了搜索效率。文中提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个如下的 6 \times 66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:

行号 1\ 2\ 3\ 4\ 5\ 61 2 3 4 5 6

列号 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。

输入格式

一行一个正整数 nn,表示棋盘是 n \times nn×n 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入 #1

6

输出 #1复制

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

说明/提示

错解:

自己写了个DFS,最后一个测试点超时。。。

#include<bits/stdc++.h>
using namespace std;
int n;
int dx[]= {-1,1,0,0,1,1,-1,-1};
int dy[]= {0,0,-1,1,1,-1,1,-1};
int vis[20][20];
int f;
bool fun(int x,int y,int op)
{
    while(x>=1&&x<=n&&y>=1&&y<=n)
    {
        if(vis[x][y]==1)
            return false;
        x+=dx[op],y+=dy[op];
    }
    return true;
}
bool check(int x,int y)
{
    bool flag = true;
    for(int i = 0; i<8; i++)
    {
        int ax = x+dx[i];
        int ay = y+dy[i];
        flag = fun(ax,ay,i);
        if(flag == false)
            return false;
    }
    return true;
}
int step[20];
long long ans;
void dfs(int x,int num)
{
    if(num==n)
    {
        if(ans<3)
        {
            for(int i = 0;i<n;i++)
            {
                if(i==0)cout<<step[i];
                else cout<<" "<<step[i];
            }
            cout<<endl;
        }
        ans++;
        return ;
    }
    for(int i = 1; i<=n; i++)
    {
        if(check(x,i))
        {
            vis[x][i]=1;
            step[num]=i;
            dfs(x+1,num+1);
            vis[x][i]=0;
        }
    }
}
int main()
{
    cin>>n;
    dfs(1,0);
    cout<<ans<<endl;
}

正解:

 分析:要放一个棋子时没必要递归去判断三行三列以及两条对角线上是否有棋子,只需要利用初中知识。。。

开三个数组(其实应该是行一个,列一个,两条斜线两个,但是因为我们是一行一行去枚举的,所以已经保证了每行有一个,因此只需要开三个数组即可),分别为col[N],l[2*N],r[2*N]

斜线怎么表示呢?

只要在斜线上,b=X+Y,X和Y可以看作是棋子的横坐标和Y的纵坐标。

只要在这一条斜线上就一定满足,b = X-Y,此时b可能为负值,但是没关系,只需要加一个N即可保证b是正数。

#include<cstdio>
using namespace std;
int n,sum;
bool col[20],u[40],v[40];
int a[20];
void dfs(int x)
{
    if(x>n)
    {
        sum++;
        if(sum<=3)
        {
            for(int i=1;i<=n;i++)
            printf("%d ",a[i]);
            printf("\n"); 
        }
        return;
    }
    for(int i=1;i<=n;i++)
    { 
        if(!col[i]&&!u[x+i]&&!v[x-i+n])
        {
            col[i]=1;
            u[x+i]=1;
            v[x-i+n]=1;
            a[x]=i;
            dfs(x+1);
            col[i]=0;
            u[x+i]=0;
            v[x-i+n]=0;
        }
    } 
} 
int main()
{
    scanf("%d",&n);
    dfs(1);
    printf("%d",sum);
    return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值