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\n",i_total); //输出结果。 
    
    return 0;
}

你可能感兴趣的:(ZOJ)