Bzoj 1087: [SCOI2005]互不侵犯King
Bzoj 1087: [SCOI2005]互不侵犯King
题链
今天下午没有事情,学习了一下\(状压DP\).感觉非常nice.
设\(f[i][j][k]\)表示到达第\(i\)第\(j\)个状态使用了\(k\)的国王的方案数.
首先筛出行合法的状态.然后二进制操作.
我这里设\(a\)为合法状态.
转移的时候.
\(a[i] \& a[j]\),\((a[i] << 1) \& a[j]\) ,\(( (a[j] << 1) \& a[i] )\)
如果这三者等于\(0\)的话,状态可以转移.
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxN = 16 + 7;
const int max_num = 1030;
int n,k;
long long f[18][2000][100];
int cnt ;
int Q[40];
int a[2000];
int tal[2000];
void work() {
int num = 0,tmp = 0;
for(int i = 0;i < n;++ i) {
if(Q[i]) num += (1 << i),tmp ++ ;
}
cnt ++;
a[cnt] = num;
tal[cnt] = tmp;
return ;
}
void dfs(int tot) {
if(tot == n) {work();return ;}
dfs(tot + 1);
if(!Q[tot - 1]) {
Q[tot] = 1;
dfs(tot + 1);
}
Q[tot] = 0;
return ;
}
int main()
{
scanf("%d%d",&n,&k);
Q[0] = 1;
dfs(1);
Q[0] = 0;
dfs(1);
for(int i = 1;i <= cnt;++ i) f[1][i][tal[i]] = 1;
for(int i = 2;i <= n;++ i) {
for(int j = 1;j <= cnt;++ j) {
for(int l = 1;l <= cnt;++ l) {
if(a[j] & a[l]) continue;
if((a[j] << 1) & a[l]) continue;
if((a[j] >> 1) & a[l]) continue;
for(int gs = tal[j];gs <= k;++ gs) {
f[i][j][gs] += f[i - 1][l][gs - tal[j]];
}
}
}
}
long long ans = 0;
for(int i = 1;i <= cnt;++ i) {
ans += (long long)f[n][i][k];
}
printf("%lld",ans);
return 0;
}
我们发现\(n,m\)特别小.显然是可以搜索暴力枚举出来的.
#include <iostream>
#include <cstdio>
const int maxN = 100;
long long ans[maxN][maxN] = {
{0},
{1,1},
{1,4,0,0,0},
{1,9,16,8,1,0,0,0,0,0},
{1,16,78,140,79,0,0,0,0,0,0,0,0,0,0,0,0},
{1,25,228,964,1987,1974,978,242,27,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,36,520,3920,16834,42368,62266,51504,21792,3600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,49,1020,11860,85275,397014,1220298,2484382,3324193,2882737,1601292,569818,129657,18389,1520,64,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,64,1806,29708,317471,2326320,12033330,44601420,119138166,229095676,314949564,305560392,204883338,91802548,25952226,4142000,281571,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,81,2968,65240,962089,10087628,77784658,450193818,1979541332,6655170642,17143061738,33787564116,50734210126,57647295377,49138545860,31122500764,14518795348,4959383037,1237072414,224463798,29275410,2673322,163088,6150,125,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
int main() {
int n,m;
scanf("%d%d",&n,&m);
printf("%lld",ans[n][m]);
return 0;
}