SSL 1344 Knights 最大匹配

一张大小为n*n的国际象棋棋盘,上面有一些格子被拿走了,棋盘规模n不超过200。马的攻击方向如下图,其中S处为马位置,标有X的点为该马的攻击点。

你的任务是确定在这个棋盘上放置尽可能多的马,并使他们不互相攻击。

分析:首先必须得想到,国际象棋的棋盘是分黑白的,也就是一格黑一格白这样。然后每只马能攻击到的位置一定是与它所在的格子异色的格子。所以我们就把棋盘根据颜色构造二分图。然后题目要求的是最多的马不能互相攻击,也就是所谓的最大独立集。而二分图中的最大独立集等于总数减去最大匹配数。

而码完后我发现数组不够,因为二分图每边最多有20000个点,如果开20000*20000的话……但我们考虑到每个点都至多与八个点相连且这些点都可以直接求,于是就在每次匹配的时候求边就好了。

下面附代码:

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,white,black,ans,k:longint;
  v1:array[1..200,1..200] of boolean;
  v:array[1..20000] of boolean;
  num:array[1..200,1..200] of longint;
  link:array[1..20000] of longint;
  b,w:array[1..20000,1..2] of longint;

function find(x1:longint):boolean;
var
  p,i,s,x,y:longint;
begin
  find:=false;
  for i:=1 to 8 do
  begin
    x:=b[x1,1]+dx[i];
    y:=b[x1,2]+dy[i];
    if (x<1)or(x>n)or(y<1)or(y>n) then continue;
    if v1[x,y]=false then continue;
    s:=num[x,y];
    if v[s] then
    begin
      p:=link[s];
      link[s]:=x1;
      v[s]:=false;
      if (p=0)or(find(p)) then exit(true);
      link[s]:=p;
    end;
  end;
end;

begin
  readln(n,m);
  fillchar(v1,sizeof(v1),true);
  for i:=1 to m do
  begin
    readln(x,y);
    v1[x,y]:=false;
  end;
  for i:=1 to n do
    for j:=1 to n do
    begin
      if v1[i,j]=false then continue;
      if (i mod 2=1)and(j mod 2=0)or(i mod 2=0)and(j mod 2=1)
        then begin
               inc(white);
               w[white,1]:=i;
               w[white,2]:=j;
               num[i,j]:=white;
             end
        else begin
               inc(black);
               b[black,1]:=i;
               b[black,2]:=j;
               num[i,j]:=black;
             end;
    end;
  for i:=1 to black do
  begin
    fillchar(v,sizeof(v),true);
    if find(i) then inc(ans);
  end;
  writeln(white+black-ans);
end.


你可能感兴趣的:(SSL 1344 Knights 最大匹配)