网络流24题之二十四 骑士共存 最大独立集

问题描述:
在一个 n*n 个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示。棋盘
上某些方格设置了障碍,骑士不得进入。
编程任务:
对于给定的 n*n 个方格的国际象棋棋盘和障碍标志, 计算棋盘上最多可以放置多少个骑
士,使得它们彼此互不攻击。
数据输入:
由文件 input.txt 给出输入数据。第一行有 2 个正整数 n 和 m (1<=n<=200, 0<=m<n 2 ),
分别表示棋盘的大小和障碍数。接下来的 m 行给出障碍的位置。每行 2 个正整数,表示障
碍的方格坐标。
结果输出:
将计算出的共存骑士数输出到文件 output.txt。
输入文件示例  输出文件示例
input.txt  
3 2
1 1

3 3

output.txt

5

【问题分析】
二分图最大独立集,转化为二分图最大匹配,从而用最大流解决。
【建模方法】
首先把棋盘黑白染色,使相邻格子颜色不同。把所有可用的黑色格子看做二分图X集合中顶点,可用的白色格子看做Y集合顶点。建立附加源S汇T,从S向X集合中每个顶点连接一条容量为1的有向边,从Y集合中每个顶点向T连接一条容量为1的有向边。从每个可用的黑色格子向骑士一步能攻击到的可用的白色格子连接一条容量为无穷大的有向边。求出网络最大流,要求的结果就是可用格子的数量减去最大流量。


代码:

const
  dx:array[1..8] of longint=(1,1,-1,-1,2,2,-2,-2);
  dy:array[1..8] of longint=(2,-2,2,-2,1,-1,1,-1);

var
  n,m,x,y,i,j,k,ans,e,s,t:longint;
  cur,last,d,num,fa:array[0..50000] of longint;
  a:array[1..200,1..200] of boolean;
  side:array[1..500000] of record
    x,y,c,op,next:longint;
  end;

procedure add(x,y,c:longint);
begin
  inc(e);
  side[e].x:=x; side[e].y:=y; side[e].c:=c; side[e].op:=e+1;
  side[e].next:=last[x]; last[x]:=e;
  inc(e);
  side[e].x:=y; side[e].y:=x; side[e].c:=0; side[e].op:=e-1;
  side[e].next:=last[y]; last[y]:=e;
end;

procedure remark(x:longint);
var
  min,i:longint;
begin
  min:=n*n-m+1;
  cur[x]:=last[x];
  i:=cur[x];
  while i>0 do
    with side[i] do
    begin
      if (c>0)and(d[y]<min) then min:=d[y];
      i:=next;
    end;
  d[x]:=min+1;
end;

procedure change;
var
  min,i:longint;
begin
  min:=maxlongint;
  i:=t;
  while i<>s do
    with side[fa[i]] do
    begin
      if c<min then min:=c;
      i:=x;
    end;
  ans:=ans+min;
  i:=t;
  while i<>s do
    with side[fa[i]] do
    begin
      dec(c,min);
      inc(side[op].c,min);
      i:=x;
    end;
end;

procedure sap;
var
  i:longint;
begin
  for i:=s to t do
    cur[i]:=last[i];
  num[0]:=n*n-m+2;
  i:=s;
  while d[s]<n*n-m+2 do
  begin
    while cur[i]>0 do
      with side[cur[i]] do
        if (c>0)and(d[y]+1=d[x])
          then break
          else cur[i]:=next;
    if cur[i]=0
      then begin
             dec(num[d[i]]);
             if num[d[i]]=0 then break;
             remark(i);
             inc(num[d[i]]);
             if i<>s then i:=side[fa[i]].x;
           end
      else begin
             fa[side[cur[i]].y]:=cur[i];
             i:=side[cur[i]].y;
             if i=t then
             begin
               change;
               i:=s;
             end;
           end;
  end;
end;

begin
  assign(input,'kni.in');
  assign(output,'kni.out');
  reset(input);
  rewrite(output);
  readln(n,m);
  fillchar(a,sizeof(a),true);
  for i:=1 to m do
  begin
    readln(x,y);
    a[x,y]:=false;
  end;
  s:=0;
  t:=n*n+1;
  for i:=1 to n do
    for j:=1 to n do
      if (i mod 2=j mod 2)and(a[i,j])
        then begin
               for k:=1 to 8 do
               begin
                 x:=i+dx[k];
                 y:=j+dy[k];
                 if (x<1)or(x>n)or(y<1)or(y>n) then continue;
                 if a[x,y]=false then continue;
                 add((i-1)*n+j,(x-1)*n+y,1);
               end;
               add(s,(i-1)*n+j,1);
             end
        else if a[i,j] then add((i-1)*n+j,t,1);
  sap;
  writeln(n*n-m-ans);
  close(input);
  close(output); 
end.


你可能感兴趣的:(网络流24题之二十四 骑士共存 最大独立集)