bzoj1087[SCOI2005]互不侵犯King 经典状压dp

来源:互联网 发布:linux移植 编辑:程序博客网 时间:2024/05/19 17:57

DP百题进度:8/100

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1087

1087: [SCOI2005]互不侵犯King

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 4488  Solved: 2599
[Submit][Status][Discuss]

Description

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

Input

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

Output

  方案数。

Sample Input

3 2

Sample Output

16

HINT

一道十分经典的状态压缩dp题,题解应该遍地都是吧_(:з」∠)_

但是鉴于本渣状压dp实在是太弱了,所以还是写一篇加深印象吧。

首先能想到dp[i][j]表示前i行放了j个国王的方案数,但很显然我们无法维护上一行对当前行的影响,所以要加一维二进制数state来维护上一行的状态。

通过枚举第一行的状态和当前行的状态,我们可以推出转移方程:dp[i][j][state]+=dp[i-1][j-num][pre];

然后就很好写了,注意判断状态不合法的情况。

代码:

#include<bits/stdc++.h>using namespace std;typedef long long LL;int read(){char c;int sum=0,f=1;c=getchar();while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}return sum*f;}int n,k;LL dp[10][90][1<<10],ans;int count1(int x){int ans=0;while(x){if(x&1)ans++;x>>=1;}return ans;}bool check(LL x,LL y){if(x&(y<<1))return false;if(x&(y>>1))return false;if(x&y)return false;return true;}void DP(){dp[0][0][0]=1;for(int i=1;i<=n;i++)for(int j=0;j<=k;j++)for(int state=0;state<=(1<<n)-1;state++){int num=count1(state);if(num>j) continue;if(state&(state>>1)) continue;for(int pre=0;pre<=(1<<n)-1;pre++){if(!check(pre,state)) continue;if(pre&(pre>>1)) continue;dp[i][j][state]+=dp[i-1][j-num][pre];}}}int main(){n=read();k=read();DP();for(int state=0;state<=(1<<n)-1;state++)ans+=dp[n][k][state];cout<<ans<<endl;return 0;}
妈耶,状压dp不是方程极难想就是代码极难写。

阅读全文
0 0
原创粉丝点击