洛谷 P1896 [SCOI2005]互不侵犯 状压dp

题目描述

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

输入输出格式

输入格式:

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

输出格式:

所得的方案数

输入输出样例

输入样例#1: 
3 2
输出样例#1: 
16



题解

状压dp入门题。关于状压dp的基本概念在此不赘述。

#include<cstdio>
using namespace std;
int n,k,num[512];
long long f[10][82][512];//十年OI一场空 不开longlong见祖宗
bool flag[512]={0};
long long ans=0;
void init()//预处理
{
for(int i=0;i<(1<<n);i++)枚举一行可能存在的状态是否合法
{
if(!(i&(i<<1)))
{
flag[i]=true;
int t=i;
while(t)如果该状态合法 则计算该状态中国王的个数
{
num[i]+=(t&1);
t>>=1;
}
f[1][num[i]][i]=1;
}
}
}
int main()
{
scanf("%d%d",&n,&k);
init();
for(int i=2;i<=n;i++)//第一行已经算过了 所以从第二行开算
for(int j=0;j<=k;j++)//k为前i行共有的国王数
for(int now=0;now<(1<<n);now++)//枚举第i行的状态
{
if(!flag[now]||num[now]>j)continue;//该状态不合法 则跳过
for(int pre=0;pre<(1<<n);pre++)//暴力枚举上一行的状态 如果上一行状态与

                                                                                       当前行状态冲突 则跳过
{
if(!flag[pre]||(now&pre)||((now<<1)&pre)||((now>>1)&pre))continue;
f[i][j][now]+=f[i-1][j-num[now]][pre];//状态转移方程
}
}
for(int i=0;i<(1<<n);i++)ans+=f[n][k][i];//枚举最后一行的状态 方案数相加 注意要开longlong
printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/R_I_P_/article/details/80215222