日常训练20161013 棋盘上的象

本文探讨了一道经典的计数问题——如何计算在n*n的棋盘上摆放k个象的方案数量,使得它们互相不可攻击。通过巧妙地将棋盘分为黑白两部分并采用动态规划的方法,成功地将问题的复杂度降低。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:求将k个象摆在nn的棋盘上,保证他们互相不能攻击的方案数。(n8,0kn2)

一开始想用类似8皇后的位运算来做,但皇后每行只能放一个,象每行可以放多个,同样的方法枚举复杂度就会高很多。复杂度无法估计,每层转移2n但不满,反正这种复杂度只过了70%的点。

var
  n,k,limit:longint;
  ans:int64;
function count(x:longint):longint;
  begin
    count:=0;
    while x>0 do
      begin
        inc(count,x and 1);
        x:=x>>1;
      end;
  end;
procedure dfs(dep,left,right,sum:longint);
  var
    can,now:longint;
  begin
    if (dep>n) then
      begin
        inc(ans,ord(sum=k));
        exit;
      end;
    can:=(limit xor (left or right));
    now:=can;
    while now>0 do
      begin
        if sum+count(now)<=k
          then dfs(dep+1,((left or now)<<1) and limit,(right or now)>>1,sum+count(now));
        now:=(now - 1) and can;
      end;
    if sum+count(now)<=k
      then dfs(dep+1,(left<<1) and limit,right>>1,sum+count(now));
  end;
begin
  assign(input,'elephant.in');reset(input);
  assign(output,'elephant.out');rewrite(output);
  read(n,k);
  limit:=1<<n-1;
  dfs(1,0,0,0);
  writeln(ans);
  close(input);close(output);
end.

实际上如果把棋盘斜过来,黑白染色,分成两块棋盘,每块棋盘上的象其实就相当于車了,而且上下行可以互换,那样把两个棋盘的行都按照从小到大排列,那么一个棋盘的每行格子数就是1 1 3 3 5 5......另一个棋盘每行格子数为2 2 4 4,这样就可以n2dp,用f[i][j]表示前i行取了j个有多少种方案,最后统计答案只要枚举一下每个棋盘上放了多少个棋子即可。

const
  MAXN=8;
var
  f,g:array[0..MAXN*2,0..MAXN*2] of int64;
  a,b:array[0..MAXN*2] of int64;
  n,k,i,j:longint;
  ans:int64;
procedure print(x:int64);
  begin
    writeln(x);
    close(input);close(output);
    halt;
  end;
function max(a,b:longint):longint;
  begin if (a>b) then exit(a) else exit(b); end;
function min(a,b:longint):longint;
  begin if (a<b) then exit(a) else exit(b); end;
begin
  assign(input,'elephant.in');reset(input);
  assign(output,'elephant.out');rewrite(output);
  read(n,k);
  if (n=1) then print(1);
  if (k>(n-1)<<1) then print(0);
  for i:=1 to n do
    if (i and 1=1)
      then a[i]:=i
      else a[i]:=a[i-1];
  for i:=1 to n-1 do
    if (i and 1=1)
      then b[i]:=i+1
      else b[i]:=b[i-1];
  f[0][0]:=1;
  for i:=0 to n-1 do
    for j:=0 to a[i] do
      begin
        inc(f[i+1][j],f[i][j]);
        inc(f[i+1][j+1],f[i][j] * (a[i+1] - j));
      end;
  g[0][0]:=1;
  for i:=0 to n-2 do
    for j:=0 to b[i] do
      begin
        inc(g[i+1][j],g[i][j]);
        inc(g[i+1][j+1],g[i][j] * (b[i+1] - j));
      end;
  for i:=max(0,k-b[n-1]) to min(a[n],k) do
    inc(ans,f[n][i] * g[n-1][k-i]);
  print(ans);
  close(input);close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值