BZOJ1087: [SCOI2005]互不侵犯King(状态压缩动态规划)
www.cnblogs.com/shaokele/
1087: [SCOI2005]互不侵犯King##
Time Limit: 10 Sec Memory Limit: 162 MB
Description###
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上
左下右上右下八个方向上附近的各一个格子,共8个格子。Input###
只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)
Output###
方案数。
Sample Input###
3 2
Sample Output###
16
题目地址 BZOJ1087: [SCOI2005]互不侵犯King
题目大意:题面已经很简洁了(逃
题解:####
先预处理出每一层状态与下一层状态之间的关系,dp转移
状态: \(dp[i][j][k]\) 表示第 \(i\) 层取了 \(j\) 个国王,这层状态为 \(k\)
转移:详见标程
c++运算优先级一定要注意啊!
#include <cstdio>
#define ll long long
using namespace std;
const int N=512;
int n,m,all;
int cnt[N];
ll dp[10][100][N];
bool fl[N],mat[N][N];
void init(){
for(int i=0;i<=all;i++)
if((i&(i>>1))==0){ //排除同层国王的冲突情况
int s=0;
for(int x=i;x;x>>=1) //记录一层放国王的数量
s+=x&1;
cnt[i]=s;fl[i]=1;
}
for(int i=0;i<=all;i++)if(fl[i])
for(int j=0;j<=all;j++)if(fl[j])
if(((i&j)==0) && ((i&(j>>1))==0) && ((j&(i>>1))==0))
mat[i][j]=1; //处理状态的转移
}
int main(){
scanf("%d%d",&n,&m);
all=(1<<n)-1;
init();
for(int i=0;i<=all;i++)
if(fl[i])
dp[1][cnt[i]][i]=1;
for(int k=1;k<n;k++)
for(int i=0;i<=all;i++)if(fl[i])
for(int j=0;j<=all;j++)if(fl[j]) //枚举两层的状态
if(mat[i][j])
for(int p=cnt[i];p+cnt[j]<=m;p++) //p表示到第i层去了几个国王
dp[k+1][p+cnt[j]][j]+=dp[k][p][i]; //转移
ll ans=0;
for(int i=0;i<=all;i++)
ans+=dp[n][m][i];
printf("%lld\n",ans);
return 0;
}