算法:位运算+DP
分析:不少人看到这道题的第一思路大概都是搜索吧,我也不例外,其实可以用一个状态压缩。
设当前位置放置国王为1,不放置为0,通过读题不难得出一定不能出现连续的1,否则国王会被吃掉。因此我们预处理哪两种状态不能同时成立,最后枚举一下就能算出哪些能成立。
分析:不少人看到这道题的第一思路大概都是搜索吧,我也不例外,其实可以用一个状态压缩。
设当前位置放置国王为1,不放置为0,通过读题不难得出一定不能出现连续的1,否则国王会被吃掉。因此我们预处理哪两种状态不能同时成立,最后枚举一下就能算出哪些能成立。
用f[i,j,k]表示前i行第i行的状态为j,放置了k个国王的种类数,最后我们统计一下f[n,i,m]即可(0<=i<=(1 shl n)-1)。
program BZOJ1087;
const
maxn=100;
maxk=100;
maxm=600;
var
ans:int64;
n,m,zt:longint;
f:array [0..maxn,0..maxm,0..maxk] of longint;
b:array [0..maxm,0..maxm] of boolean;
function can1(x:longint):boolean;
var
i:longint;
s:ansistring;
begin
s:=binstr(x,n);
for i:=1 to n do if (s[i]='1') and (((s[i]=s[i-1]) and (i>1)) or ((s[i]=s[i+1]) and (i<n))) then exit(false);
exit(true);
end;
function can2(x1,x2:longint):boolean;
var
i:longint;
s1,s2:ansistring;
begin
s1:=binstr(x1,n);
s2:=binstr(x2,n);
for i:=1 to n do if (s1[i]='1') and ((s2[i]='1') or (s2[i-1]='1') or (s2[i+1]='1')) then exit(false);
exit(true);
end;
function calc(x:longint):longint;
var
i:longint;
s:ansistring;
begin
calc:=0;
s:=binstr(x,n);
for i:=1 to n do if s[i]='1' then inc(calc);
end;
procedure init;
var
i,j:longint;
begin
readln(n,m);
zt:=(1 shl n)-1;
for i:=0 to zt do f[1,i,calc(i)]:=1;{初始化都为1。}
for i:=0 to zt do for j:=0 to zt do if can1(i) and can1(j) and can2(i,j) then b[i,j]:=true;{枚举哪种状态可以成立。}
end;
procedure main;
var
i,j,k,l:longint;
begin
for i:=2 to n do
begin
for j:=0 to zt do
begin
for k:=0 to zt do
begin
for l:=0 to m do
begin
if (b[j,k]) and (l>=calc(j)) then{当前放置的国王个数必须大于之前的国王个数,同时这两种状态必须能同时成立。}
inc(f[i,j,l],f[i-1,k,l-calc(j)]);
end;
end;
end;
end;
for i:=0 to zt do inc(ans,f[n,i,m]);
end;
begin
init;
main;
writeln(ans);
end.