zoukankan      html  css  js  c++  java
  • P1219 八皇后 含优化 1/5

    题目描述

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

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

    行号 1 2 3 4 5 6

    列号 2 4 6 1 3 5

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

    //以下的话来自usaco官方,不代表洛谷观点

    特别注意: 对于更大的N(棋盘大小N x N)你的程序应当改进得更有效。不要事先计算出所有解然后只输出(或是找到一个关于它的公式),这是作弊。如果你坚持作弊,那么你登陆USACO Training的帐号删除并且不能参加USACO的任何竞赛。我警告过你了!

    输入输出格式

    输入格式:

    一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。

    输出格式:

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

    输入输出样例

    输入样例#1: 复制
    6
    
    输出样例#1: 复制
    2 4 6 1 3 5
    3 6 2 5 1 4
    4 1 5 2 6 3
    4



    紫书上的八皇后
    普通方法 555ms
    #include<iostream>//个人不建议采用头文件,可能和定义的变量或名字起冲突,从而引起编译错误;
    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int a[100],b[100],c[100],d[100];
    //a数组表示的是行;
    //b数组表示的是列;
    //c表示的是左下到右上的对角线;
    //d表示的是左上到右下的对角线;
    int total;//总数:记录解的总数
    int n;//输入的数,即N*N的格子,全局变量,搜索中要用
    int print()
    {
        if(total<=2)//保证只输出前三个解,如果解超出三个就不再输出,但后面的total还需要继续叠加
        {
            for(int k=1;k<=n;k++)
            cout<<a[k]<<" ";//for语句输出
            cout<<endl;
        }
        total++;//total既是总数,也是前三个排列的判断
    }
    void queen(int i)//搜索与回溯主体
    {
        if(i>n)
        {
            print();//输出函数,自己写的
            return;
        }
        else
        {
            for(int j=1;j<=n;j++)//尝试可能的位置
            {
                if((!b[j])&&(!c[i+j])&&(!d[i-j+n]))//如果没有皇后占领,执行以下程序
                {
                    a[i]=j;//标记i排是第j个
                    b[j]=1;//宣布占领纵列
                    c[i+j]=1;
                    d[i-j+n]=1;
                    //宣布占领两条对角线
                    queen(i+1);//进一步搜索,下一个皇后
                    b[j]=0;
                    c[i+j]=0;
                    d[i-j+n]=0;
                    //(回到上一步)清除标记
                }
            }
        }
    }
    int main()
    {    
        cin>>n;//输入N*N网格,n已在全局中定义
        queen(1);//第一个皇后
        cout<<total;//输出可能的总数
        return 0;
    }
    View Code
    加速  99ms
    #include<cstdio>
    int upperlim,n,ans,tot,plan[15];
    void dfs(int i,int c,int ld,int rd)//变量含义:第i行(只是用来记录方案,如果不输出方案,可以去掉),c是行影响,ld、rd分别是左右对角线
    {
      if(c==upperlim)//很好理解,如果是一个可行解的话自然所有列上都有棋子,如果不是可行解,看下面
      {
        if(++ans<=3)
        {
          for(int i=1;i<n;i++)
            printf("%d ",plan[i]);
          printf("%d
    ",plan[n]);
        }
        return;
      }
      int mask=upperlim&~(ld|rd|c);//mask指的是留下来能用的位置,受到行、左右对角线的影响。用upperlim & 则是要舍掉高位上多出来的1
      while(mask)//尽管是穷举位,但是不用判断,因为如果不可行的话mask上没有能放的位置,根本到达不了最终状态就退出,位运算解法天然剪枝的高效、简洁是别的解法永远也追不上的
      {
        int p=mask&(-mask);//技巧,表示mask末尾的第一个1,也就是穷举能放的位置
        mask-=p;//
        plan[i]=__builtin_ffs(p);//__builtin_ffs(p)指的是p的末尾第一个1的位置,竞赛能不能用我不知道,但是据说有人WinterCamp用了AC
        dfs(i+1,c|p,(ld|p)<<1,(rd|p)>>1);//状态转移,c|p是行上影响,ld:对角线上首先增加了p的影响很好理解,左移则是对角线向下一行的自然延伸,rd同理
      }
    }
    //尽管注释很详细,但是还是自己敲2,3遍才能理解
    int main()
    {
      scanf("%d",&n);
      upperlim=(1<<n)-1;//目标状态
      ans=0;
      dfs(1,0,0,0);
      printf("%d
    ",ans);
      return 0;
    }
    View Code


  • 相关阅读:
    SP笔记:交叉实现七行并成一行
    HTML tag 学习
    操作哈希表
    Efficient bipedal robots based on passivedynamic walkers
    Pushing People Around
    ZEROMOMENT PONTTHIRTY FIVE YEARS OF ITS LIFE

    Active Learning for RealTime Motion Controllers
    Accelerometerbased User Interfaces for the Control of a Physically Simulated Character
    Dynamic Response for Motion Capture Animation
  • 原文地址:https://www.cnblogs.com/bxd123/p/10531870.html
Copyright © 2011-2022 走看看