[CodeVs1050]棋盘染色2(状态压缩DP)

      题目大意:有一个5*N(≤100)的棋盘,棋盘中的一些格子已经被染成了黑色,求最少对多少格子染色,所有的黑色能连成一块。

      这题卡了我1h,写了2.6k的代码,清明作业一坨还没做啊。。。之前一直以为这题是插头DP,结果今天一看发现不用>_<,虽然还是状压DP。

      因为只有5列,所以每行至多有3个黑色联通块,即黑,白,黑,白,黑,其他的情况都少于3个联通块了,所以我们可以把联通块标号。0表示白色,1表示1号联通块,2和3同理,所以我们可以用4进制来表示每一行的状态。则下一行的黑色若与上一行的黑色连接,它的联通块编号即上一行与其连接的黑色的联通块编号。对于每一个状态,枚举下一层要涂黑哪个白色,然后转移,最后一层所有黑色为同一联通块就更新答案。这样这道题就做完了,思路很简单,但是写起来确实有点麻烦。。。不过写那么久一定是我太弱了= =。。。

代码如下:

type
  node=array[0..6]of longint;
var
  f:array[0..100,0..2000]of longint;
  a:array[0..100]of longint;
  h:array[0..102400,1..2]of longint;
  s,t:node;
  ch:char;
  n,i,j,ans:longint;

function lowbit(x:longint):longint;
begin
  if x=0 then exit(0);
  exit(lowbit(x-(x and -x))+1);
end;

procedure change(var a:node;sum1,sum2:longint);
var
  i:longint;
begin
  for i:=1 to 5 do
  if a[i]=sum1 then
  begin
    a[i]:=sum2;
    if (a[i-1]<>0)and(a[i-1]<10) then change(a,a[i-1],sum2);
    if (a[i+1]<>0)and(a[i+1]<10) then change(a,a[i+1],sum2);
  end;
end;

procedure work(var a:node);
var
  i,sum:longint;
begin
  sum:=10;
  for i:=1 to 5 do
  if (a[i]<>0)and(a[i]<10) then
  begin
    inc(sum);
    change(a,a[i],sum);
  end;
  for i:=1 to 5 do
  if a[i]>0 then dec(a[i],10);
end;

procedure bfs;
var
  i,j,k,yy,front,rear:longint;
  flag:boolean;
begin
  h[1][1]:=0;h[1][2]:=0;
  front:=0;rear:=1;
  while front<rear do
  begin
    inc(front);
    yy:=h[front][2];
    for i:=1 to 5 do
    begin
      s[i]:=h[front][2] and 3;
      h[front][2]:=h[front][2]>>2;
    end;
    h[front][2]:=yy;
    if h[front][1]=n then
    begin
      flag:=true;
      for i:=1 to 5 do
      if s[i]>1 then flag:=false;
      if flag then
      if ans>f[h[front][1],h[front][2]] then ans:=f[h[front][1]][h[front][2]];
      continue;
    end;
    for i:=0 to (1<<5)-1 do
    if i and a[h[front][1]+1]=0 then
    begin
      for j:=1 to 5 do
      t[j]:=(((a[h[front][1]+1]+i)>>(j-1))and 1)*(j+3);
      k:=0;
      for j:=1 to 5 do
      if (s[j]>0) and (t[j]>0) then
      begin
        t[j]:=s[j];
        k:=k or (1<<s[j]);
      end;
      flag:=true;
      for j:=1 to 5 do
      if (s[j]>0)and(k and (1<<s[j])=0) then flag:=false;
      if flag=false then continue;
      work(t);
      k:=0;
      for j:=5 downto 1 do
      k:=k<<2+t[j];
      if f[h[front][1]+1,k]>1000000000 then
      begin
        inc(rear);
        h[rear][1]:=h[front][1]+1;
        h[rear][2]:=k;
      end;
      if f[h[front][1]+1][k]>f[h[front][1]][h[front][2]]+lowbit(i) then
      f[h[front][1]+1][k]:=f[h[front][1]][h[front][2]]+lowbit(i);
    end;
  end;
end;

begin
  readln(n);
  for i:=1 to n do
  begin
    for j:=1 to 5 do
    begin
      read(ch);
      if ch='1' then a[i]:=a[i] or 1<<(j-1);
    end;
    readln;
  end;
  while n>0 do
  begin
    if a[n]>0 then break;
    dec(n);
  end;
  if n=0 then
  begin
    writeln(0);
    halt;
  end;
  fillchar(f,sizeof(f),63);
  t[0]:=0;t[6]:=0;
  ans:=maxlongint;
  f[0,0]:=0;
  bfs;
  writeln(ans);
end.
View Code

       看样子我的代码在Pascal党里算是很短的了。。。而且我的代码还有饱受机房神犇吐槽的begin打在下一行,如下图,代码长度在最后一栏

原文地址:https://www.cnblogs.com/Sakits/p/5350153.html