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; }