P1219 [USACO1.5]八皇后 Checker Challenge

在这里插入图片描述.在这里插入图片描述

题目本身不难,还是我太弱了,AC半天都不成。。。踩坑无数,第一个TL了4个,第二次改进后TL了最后一个。还踩坑无数,踩坑很隐蔽,例如下面这个很简单的程序~

在这里插入图片描述
很多人都以为是输出1,这个点害得我找了好久好久的错误,你单独输出temp.size()都没啥问题,就是不能abs(0-temp.size()) 他的输出结果。

在这里插入图片描述
输出这个之前先强转一波:

在这里插入图片描述
在这里插入图片描述size_t类型又像int,又不是,用的时候一定要记得强转一波,我被这个点坑死了.这道题有种非常简单的方法,我从算法笔记上面看到的。思路:因为题目说了不能同行不能同列也不能同对角线,不能同行同列都很简单,就是同对角线不好判断,这道题的直接思路是:从n*n的棋盘中选取n个,可想而知,肯定会TL,我们可以这样做,只需要对行号进行一个全排列即可(对于每一列,我们都对应一个唯一的行号),然后判断这种对角线是否成立。第一次代码:

#include<iostream>
#include <vector>
#include <cstring>
#include <cmath>
int cnt=0;
using namespace std;
bool judge(vector<int > temp);
void dfs(vector<vector<int> > &res,int n,int level,bool flag[],vector<int> temp);
    int main()
    {
        int n;
        cin>>n;
        vector<vector<int > >res;
        bool flag[n+1];
        memset(flag,false, sizeof(flag));
        vector<int > temp;
        dfs(res,n,0,flag,temp);
        for(int i=0;i<=2;i++)
        {
            for(int j=0;j<=res[i].size()-1;j++)
            {
                cout<<res[i][j]<<" ";
            }
            cout<<endl;
        }
        cout<<cnt<<endl;
        return 0;
    }

    void dfs(vector<vector<int> > &res,int n,int level,bool flag[],vector<int> temp)
    {
        if(level==n)
        {
            if(judge(temp))
            {
                if(cnt<=2)
                {
                    res.push_back(temp);
                }
                cnt++;
            }

        }
        for(int i=1;i<=n;i++)
        {
            if(!flag[i])
            {
                temp.push_back(i);
                flag[i]=true;
                dfs(res,n,level+1,flag,temp);
                flag[i]=false;
                temp.pop_back();
            }
        }
    }

    bool judge(vector<int > temp)
    {
        for(int i=0;i<=temp.size()-1;i++)
        {
            for(int j=i+1;j<=temp.size()-1;j++)
            {
                if(abs(i-j)==abs(temp[i]-temp[j]))
                {
                    return false;
                }

            }
        }
        return true;
    }

judge方法,如果两个皇后处在对角线上,那么他们的横坐标之差和他们的纵坐标之差一定相等.不会的可以自己画画图~

上面代码是将n列的每一行选完以后才判断的,时间肯定会超时,果不其然,TL掉了4个点,我们可以这样,再选第i列元素的时候,判断i列的那一行是否和之前的产生冲突。这种不用等到全部选完才去判断,而是边选边判断,并且选完以后一定是合法的。代码如下

#include<iostream>
#include <vector>
#include <cstring>
#include <cmath>
int cnt=0;
using namespace std;
bool judge(vector<int > temp);
void dfs(vector<vector<int> > &res,int n,int level,bool flag[],vector<int> temp);
    int main()
    {
        int n;
        cin>>n;
        vector<vector<int > >res;
        bool flag[n+1];
        memset(flag,false, sizeof(flag));
        vector<int > temp;
        dfs(res,n,0,flag,temp);
        for(int i=0;i<=2;i++)
        {
            for(int j=0;j<=res[i].size()-1;j++)
            {
                cout<<res[i][j]<<" ";
            }
            cout<<endl;
        }
        cout<<cnt<<endl;
        return 0;
    }

    void dfs(vector<vector<int> > &res,int n,int level,bool flag[],vector<int> temp)
    {
        if(level==n)
        {
            if(cnt<=2)
            {
                res.push_back(temp);
            }
            cnt++;
        }

        for(int i=1;i<=n;i++)
        {
            if(!flag[i])
            {
                bool fla= true;
                for(int j=0;j<int(temp.size());j++)
                {
                    if(abs(j-int(temp.size()))==abs(i-temp[j]))
                    {
                        fla=false;
                        break;
                    }
                }
                if(fla==true)
                {
                   temp.push_back(i);
                   flag[i]=true;
                   dfs(res,n,level+1,flag,temp);
                   flag[i]=false;
                   temp.pop_back();
                }
            }
        }
    }

上面代码虽然快了一点,但还是TL了最后一个点~

等会马上开始第三种深搜~未完待续

刚才写了一个简单点的写法,每次从第level列中选一行,然后将其那一行,那个元素位置的左右对角线全部标记为0,因为level表示列,也表示第level层,即从第一层选一行,然后将行号标记,选中元素的左右对角线也全部标记,接着选第level+1 层,同样的标记那一行,被选中元素的左右对角线全部标记为1…直到选好n个数为止。

如何标记左右对角线才是本题的核心,观察可以看出某一个元素(i,j) 其左对角线i-j全部相等,右对角线i+j全部相等,所以每当我们去选择第level列的第i行的时候,除了要判断一下i行是否被选中过,还要判断(level,j)的左右对角线是否有皇后. AC代码如下~

#include <iostream>
#include <cstring>
int r[14],l[14],lef[50],rig[50];
void dfs(int n,int level);
int cnt=0;
using namespace std;
    int main()
    {
        int n;
        cin>>n;
        memset(r,0, sizeof(r));
        memset(l,0, sizeof(l));
        memset(lef,0, sizeof(lef));
        memset(rig,0,sizeof(rig));
        dfs(n,1);
        cout<<cnt<<endl;
        return 0;
    }

    void dfs(int n,int level)
    {
        if(level==n+1)
        {
            if(cnt<=2)
            {
                for(int i=1;i<=n;i++)
                {
                    cout<<r[i]<<" ";
                }
                cout<<endl;
            }
            cnt++;
            return ;
        }
        for(int i=1;i<=n;i++) //选取第level列中的第i行
        {
            if(!l[i]&&!lef[i-level+n]&&!rig[i+level])  //第i列没有被选过而切其左对角线和右对角线都没有被选中
            {
                r[level]=i;
                lef[i-level+n]=1;//将左对角线标记
                rig[i+level]=1;//将右对角线标记
                l[i]=1;//将第i行标记
                dfs(n,level+1);
                lef[i-level+n]=0;
                rig[i+level]=0;
                l[i]=0;
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值