SSL 1383 车Ⅱ 状压dp

有一个n*m的棋盘(n、m≤80,n*m≤80)要在棋盘上放k(k≤20)个棋子,使得任意两个棋子不相邻。求合法的方案总数。

我们用数组s保存一行中所有的num个放置方案,则s数组可以在预处理过程中用DFS求出,同时用ci保存第i个状态中1的个数以避免重复计算。开始设计状态。本题状态的维数需要增加,原因在于并不是每一行只放一个棋子,也不是每一行都要求有棋子,原先的表示方法已经无法完整表达一个状态。我们用 fi,j,k表示第i行的状态为sj且前i行总共放置k个棋子(下面用pn代替原题中的k)的方案数。沿用枚举当前行方案的做法,只要当前行的放置方案和上一行的不冲突。微观地讲,就是要两行的状态s1和s2中没有同为1的位即可,亦即 s1 &s2 =0。然而,虽然我们枚举了第i行的放置方案,但却不知道其上一行(第i-1行)的方案。为了解决这个问题,我们不得不 连第i-1行的状态一起枚举
代码:

<pre name="code" class="plain">var
  d:array[1..10] of longint;
  a,b:array[1..100] of longint;
  f:array[0..80,0..1000,0..10] of longint;
  ans,n,m,p,i,j,k,l,a1:longint;

procedure dfs(x:longint);
var
  i:longint;
begin
  if x>m then
  begin
    inc(a1);
    for i:=1 to m do
    begin
      a[a1]:=a[a1]+(1 shl (i-1))*d[i];
      inc(b[a1],d[i]);
    end;
    exit;
  end;
  dfs(x+1);
  d[x]:=1;
  dfs(x+2);
  d[x]:=0;
end;

begin
  readln(n,m,p);
  if n<m then
  begin
    n:=n xor m; m:=n xor m; n:=n xor m;
  end;
  dfs(1);
  f[0,0,0]:=1;
  for i:=1 to n do
    for j:=0 to p do
      for k:=1 to a1 do
        if j>=b[k] then
          for l:=1 to a1 do
            if a[k] and a[l]=0 then
              f[i,a[k],j]:=f[i,a[k],j]+f[i-1,a[l],j-b[k]];
  for i:=1 to a1 do
    ans:=ans+f[n,a[i],p];
  writeln(ans);
end.


 
 

你可能感兴趣的:(SSL 1383 车Ⅱ 状压dp)