互不侵犯King(SCOI2005)

算法:位运算+DP
 
分析:不少人看到这道题的第一思路大概都是搜索吧,我也不例外,其实可以用一个状态压缩。
      设当前位置放置国王为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.




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值