• BZOJ 1087 互不侵犯king


           这道题与皇后问题极像,只是两者的攻击范围不一样,同时根据题目限制可以发现,这道题数据的特殊性,棋盘很小,因此想到用状态压缩DP的方法求解。

           首先将每一行互不侵犯的可能列出来,用1、0的方式记录,之后根据要求会发现,每一行的情况受上一行的情况限制,于是从第一行进行一层层的判断。又由于国王的攻击是一个九宫格,因此难点在于两国王处于对角,则进行判断时将下一行向左移一位或向右移一位再进行判断。最后记得国王数是一定的,用一个变量记录一下。

           程序无太大的突出,但每一步写的较清晰,希望对大家有所帮助。

    #include<cstdlib>
    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    
    #define MAXN 100 //对MAXN进行赋值 。 
    
    using namespace std;
    
    long long int f[MAXN]/*行数*/[MAXN]/*状态*/[600]/*当前国王总数*/,i_total/*最终结果*/; 
    int stay[MAXN],cnt[MAXN];
     // stay用于记录每种状态压缩后的值。 cnt用于记录对应的状态中的1的个数。
    int map[MAXN]/*i*/[MAXN]/*j*/; // 当上一状态为 i,下一状态为 j时,是否合法。 
    int i_side,i_number,i_temp=0; //边长、数量、每种状态的个数。 
    
    void pre_dfs(int x,int y,int z) 
    //预处理:x 是放了几颗国王,y 是当前放的位置,z 是当前状态压缩后的值。 
    {
        stay[++i_temp]=z; //在stay数组中进行存储,记录目前的状压值。 
        cnt[i_temp]=x; // 在cnt数组中进行存储,记录已放国王的数目 
    
        if(x>=(i_side+1)/2||x>=i_number)  //如果x超范围限制,则返回停止。 
        {
            return;
        }  
        for(int i=y+2;i<=i_side;i++) //用for循环重复所有可能,将每一种可能用递归进行记录。 
        {
            pre_dfs(x+1,i,z+(1<<(i-1)));
        }
             
    }
    void pre_map()
    {
         for(int i=1;i<=i_temp;i++)
        {
             for(int j=1;j<=i_temp;j++) 
            {
                map[i][j]=map[j][i]=((stay[i]&stay[j])||((stay[i]>>1)&stay[j])||((stay[i]<<1)&stay[j]))?0:1;
    //对 map[i][j]、 map[j][i]进行赋值,如果不移动冲突或左移冲突或右移冲突,则赋值为0,反之为1。 
            }
        } 
            
         for(int i=1;i<=i_temp;i++)
        {
              f[1][cnt[i]][i]=1; //用f数组记录处于第一行、cnt[i]的状态下、有 i个国王时的情况。 
        }      
    }
    
    int main()
    {
        scanf("%d%d",&i_side,&i_number);//输入边长及王的个数 
        
        pre_dfs(0,-1,0); //传入初始值进行深搜。 
        pre_map(); // 预处理一下每种状态。 
        
        for(int i=2;i<=i_side;i++)
        {
              for(int j=0;j<=i_number;j++) //国王必须能全部放下。 
             {
        for(int q=1;q<=i_temp;q++) //枚举上一行状态。 
                    {
                           if(cnt[q]>j)//状态国王数量要小于等于前 i行国王数量 j。 
                           {
                                   continue;
                            }  
                            for(int i_number=1;i_number<=i_temp;i_number++
                           {
                      if(map[i_number][q]&&cnt[i_number]+cnt[q]<=j)
                      {
                                            f[i][j][q]+=f[i-1][j-cnt[q]][i_number];     
                       } 
                           }        
                    }
              }   
        } 
            
        for(int i=1;i<=i_temp;i++) //计算最终值。 
        {
            i_total=i_total+f[i_side][i_number][i];
        }
             
        printf("%lld
    ",i_total); //输出结果。 
        
        return 0;
    }

  • 相关阅读:
    职业生涯规划
    Java中double函数,四舍五入并保留小数点后两位的4种方法,BMI案例
    获取request.getSession().setAttribute()的值【详解加案例】
    Mac 如何安装Homebrew?
    XCode6的iOS Simulator 文件保存位置
    iOS8无法弹出本地通知?
    Xcode如何找到默认的生成路径?
    Android 实现简单音乐播放器(二)
    Android 实现简单音乐播放器(一)
    Android 如何实现带滚动条的TextView,在更新文字时自动滚动到最后一行?
  • 原文地址:https://www.cnblogs.com/szy-wlxy/p/4620118.html
Copyright © 2020-2023  润新知