bzoj 1087: [SCOI2005]互不侵犯King 状压dp

来源:互联网 发布:c语言输出txt文件内容 编辑:程序博客网 时间:2024/05/22 00:25

Description

  在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。

Input

  只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

Output

  方案数。


           这种类型的状压dp好像非常常见,本来我想预处理出所有可行状态,这样能减少状态的,不过听说邻桌的lijiamu神犇直接暴力状压过了,就试着打了一个暴力状压真过了...

       首先考虑状态,我们用01表示这一行中放了还是没放,因为还有对于k个的限制,所以我们便可以使用dp[i][state][j]表示现在考虑到第i行,放或不放的状态为state,用了j个棋子的方案数。

       由于本题不像八皇后,攻击距离是一整列,这道题只关心上下两列,所以我们就可以对于每一个状态,从它上方的那一行进行转移。枚举当前行的状态,再枚举分到这一行总共使用了j个王,即可由上一行的可行状态以及j-当前行放的个数转译而来(当时傻傻的我将这个定义为这一行放了几个一直没想到怎么转移)。

       最终答案就是最后一行放k个所有状态的方案数之和了。

       下附AC代码。

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#define maxn 10using namespace std;typedef long long ll;ll n,k;ll dp[maxn][(1<<9)][maxn*maxn];ll count(ll now){ll cnt=0;for(ll i=0;i<=n;i++)if(((1<<i)&now)){cnt++;}return cnt;}int main(){scanf("%lld%lld",&n,&k);dp[0][0][0]=1;for(ll i=1;i<=n;i++){for(ll j=0;j<=k;j++)for(ll state=0;state<(1<<n);state++){ll coun=count(state);if((state&(state<<1))==0  && (state&(state>>1))==0 && coun<=j){for(ll pre=0;pre<(1<<n);pre++){if((pre&(pre<<1))==0  && (pre&(pre>>1))==0){if((state&pre)==0 && (state&(pre<<1))==0  && (state&(pre>>1))==0){dp[i][state][j]+=dp[i-1][pre][j-coun];}}}}}}long long ans=0;for(ll i=0;i<(1<<n);i++)ans+=dp[n][i][k];}





原创粉丝点击