BZOJ 1087:[SCOI2005]互不侵犯King(状压DP)

                                                             [SCOI2005]互不侵犯King

【题目描述】

在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案。国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子。

【输入】

只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N)

【输出】

方案数。

【样例输入】

3 2

【样例输出】

16

分析:

经典的状压DP题目,可我竟然调了很长时间都没对,后来发现是DP枚举范围错了,直接枚举到最大情况导致答案大得离谱,所以透彻理解算法很重要。

其它懒得写了。

代码1(第一次写的代码很丑,还很多余地在每个二进制首位增添一个1......):

program king;
var
  f:array[0..10,512..1023,1..100]of int64;
  a:array[512..1023,1..11]of 0..1;
  sum1,w:array[512..1023]of longint;
  g:array[512..1023,512..1023]of boolean;
  h:array[512..1023]of boolean;
  n,i,m,j,k,u,v,l,num,num1:longint;
procedure work(x:longint);
var i,c,y:longint;
begin
  i:=x; c:=10;
  while i>0 do
   begin
     a[x,c]:=i mod 2; if a[x,c]=1 then  begin inc(sum1[x]); if c>1 then y:=10-c+1; end;
     i:=i div 2;
     c:=c-1;
   end;
  dec(sum1[x]); w[x]:=y;
end;
begin
  readln(n,k);num1:=512;num:=1;
  for i:=1 to n do num:=num*2;
  num:=num1+num-1;
  for i:=num1 to num do
   begin
    work(i);
   end;
  for u:=num1 to num do
   for v:=num1 to num do
    for l:=2 to 10 do begin g[u,v]:=true;
  if (a[u,l]=1)and(((a[v,l-1]=1)and(l>2))or(a[v,l]=1)or(a[v,l+1]=1)) then begin g[u,v]:=false;break; end;
   if (a[v,l]=1)and(((a[u,l-1]=1)and(l>2))or(a[u,l]=1)or(a[u,l+1]=1)) then begin g[u,v]:=false;break; end;
   end;
  fillchar(f,sizeof(f),0);
  for i:=num1 to num do begin h[i]:=true;
   for j:=2 to 10 do
   if ((a[i,j]=1)and(((a[i,j-1]=1)and(j>2))or(a[i,j+1]=1))) then begin h[i]:=false; break; end;
   if h[i]=true then f[1,i,sum1[i]]:=1;
   end;
  for i:=2 to n+1 do
   for j:=0 to k do
    for u:=num1 to num do
     if (sum1[u]<=j)and(h[u]=true) then
     for v:=num1 to num do
      if (sum1[v]+sum1[u]<=j)and(h[v]=true) then
       begin
        if g[u,v]=true then f[i,u,j]:=f[i,u,j]+f[i-1,v,j-sum1[u]]; 
       end;
  writeln(f[n+1,num1,k]);
end.
View Code

代码2(改进的代码,用了一些位运算,比第一次好看一点):

program king;
var
  f:array[0..10,0..511,1..100]of int64;
  sum1:array[0..511]of longint;
  g:array[0..511,0..511]of boolean;
  h:array[0..511]of boolean;
  n,i,m,j,k,u,v,l,t,num:longint;
begin
  readln(n,k);
  fillchar(g,sizeof(g),false);
  fillchar(h,sizeof(h),false);
  fillchar(f,sizeof(f),0);
  num:=1;
  for i:=1 to n do num:=num*2;  num:=num-1;
  for i:=0 to num do
   if i and (i shr 1)=0 then
   begin
     j:=i;t:=0;
     while j>0 do begin t:=t+j and 1;  j:=j shr 1; end;
     sum1[i]:=t; h[i]:=true;
   end;
  for i:=0 to num do if h[i]=true then
   for j:=0 to num do if h[j]=true then
    if (i and j=0)and(i and (j shr 1)=0)and(j and (i shr 1)=0) then g[i,j]:=true;
  for i:=0 to num do if h[i]=true then f[1,i,sum1[i]]:=1;
  for i:=2 to n+1 do
   for j:=0 to k do
    for u:=0 to num do if (h[u]=true)and(sum1[u]<=j) then
      for v:=0 to num do if (h[v]=true)and(sum1[u]+sum1[v]<=j) then
       if g[v,u]=true then
        inc(f[i,u,j],f[i-1,v,j-sum1[u]]);
  writeln(f[n+1,0,k]);
end.
View Code

 

posted @ 2015-03-16 22:34  QTY_YTQ  阅读(179)  评论(0编辑  收藏  举报