标签:方案 注意 return lag include ios 范围 efi 状态压缩dp
状压DP练习题
首先注意这道题的数据范围:
$1\leq N \leq 9? ,?0 \leq K\leq N^2$
这道题求方案数,那么可以很自然的想到用dp或者搜索,然而对于本题而言,搜索需要记录的信息和状态太多,会T到天上去,所以只有采用状压DP来解决。
注意到n非常小,而且状态需要记录的信息非常多,所以考虑状态压缩dp。在这道题里,当前行的状态和上一行的状态有着密切联系,而且答案和k有关,所以我们设计状态f[i][j][k]为前i行已经放了j个国王并且第i行的状态为k(二进制)的方案数,那么$f[i][j][k] = \sum f[i - 1][j - num[k]][l]$,其中num数组记录着一行为状态k的放的国王的数目,l为上一行符合要求的状态.
现在的问题就是如何判断状态i是否符合要求,这不仅与i本身有关,还和它上一行的状态j有关,利用左移,右移,&就可以判断了,它的原理是什么呢?我们把每个状态抽象成1个二进制数,它的第i位表示第i列放不放国王,利用&的性质,可以判断上下两行是否冲突,那么如何判断当前行是否冲突呢?将i左移1位再与i进行&操作即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 1100
#define M 15
#define ll long long
using namespace std;
int n,k,num[N];
ll f[M][N][N],ans;
bool flag[N];
int main() {
scanf("%d%d",&n,&k);
for(int i = 0 ; i < (1<<n) ; i++) {
if(!(i & (i<<1))) {
flag[i] = 1;
int t = i;
while(t) {
num[i] += (t & 1);
t >>= 1;
}
f[1][num[i]][i] = 1;
}
}
for(int i = 2 ; i <= n ; i++)
for(int j = 0 ; j <= k ; j++)
for(int l = 0 ; l < (1<<n) ; l++) {
if(!flag[l]) continue;
if(num[l] > j) continue;
for(int last = 0 ; last < (1<<n) ; last++) {
if(!flag[last]) continue;
if((last & l) || (l<<1)&last || (l>>1)&last) continue;
f[i][j][l] += f[i - 1][j - num[l]][last];
}
}
for(int i = 0 ; i < (1<<n) ; i++)
ans += f[n][k][i];
printf("%lld",ans);
return 0;
}
标签:方案 注意 return lag include ios 范围 efi 状态压缩dp
原文地址:https://www.cnblogs.com/Repulser/p/9609402.html