棋盘覆盖

【题目描述】
给出一张n*n(n<=100)的国际象棋棋盘,其中被删除了一些点,问可以使用多少1*2的多米诺骨牌进行掩盖。
【输入格式】
第一行为n,m(表示有m个删除的格子)
第二行到m+1行为x,y,分别表示删除格子所在的位置
【输出格式】
一个数,即最大覆盖格数
【样例输入】
8 0
【样例输出】
32
【分析】
刚看到题目的时候想的是数论,但是删除格所在位置是程序输入的,无法用数学方法解决。然后就想到了万能的搜索,但是多米诺骨牌是1*2的(要是1*1的还要做吗),写边界写到几乎吐血还是写不对。然后看到二分图的定义就想到,对两个任意方格(看作结点),如果它们分属两个不同的集合的话,那么它们之间存在边的情况只有它们是相邻的方格的时候。这样一来,我们可以将矩阵中所有元素分成两个点集,相临点间存在边的关系。那么如何分这两个集合呢, 我们可以对棋盘进行染色(黑白),黑白相间,即相临两格不同色,这样染完色,我们就可以将同种颜色的格子作为一个集合,另外一种颜色作为一个集合,然后相临点(格子)间存在边的关系 (当然得排除挖去的部分)。这样一个二分图就建完了。然后匈牙利算法求最大匹配,用邻接表优化。
匈牙利算法思想:不停的找增广轨,并增加匹配的个数。增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条”交错轨”,也就是说这条由图的边组成的路径,它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有……最后一条边没有参与匹配,并且始点和终点还没有被选择过。这样交错进行,显然他有奇数条边。那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配……以此类推。也就是将所有的边进行”反色”,容易发现这样修改以后,匹配仍然是合法的,但是匹配数增加了一对。另外,单独的一条连接两个未匹配点的边显然也是交错轨。可以证明,当不能再找到增广轨时,就得到了一个最大匹配。这也就是匈牙利算法的思路。
算法框架:
⑴置M为空
⑵找出一条增广路径P,通过取反操作获得更大的匹配M’代替M
⑶重复⑵操作直到找不出增广路径为止

var
  map:array[1..10000,1..4] of longint;
  link,v:array[1..10000] of longint;
  cover:array[1..10000] of boolean;
  n,m,x,y,i,j,ans:longint;
  visit:array[0..101,0..101] of boolean;
function find(i:longint):boolean;
var
  k,q,m:longint;
begin
  find:=true;
  for m:=1 to v[i] do begin
      k:=map[i,m];
      if not cover[k] then begin
        q:=link[k];link[k]:=i;
            cover[k]:=true;
        if (q=0)or(find(q)) then exit;
          link[k]:=q;
          end;
    end;
    exit(false);
end;
begin
    readln(n,m);
    fillchar(v,sizeof(v),0);
    fillchar(visit,sizeof(visit),false);
    for i:=1 to n do
        for j:=1 to n do
            visit[i,j]:=true; 
    for i:=1 to m do begin
        readln(x,y);
        visit[x,y]:=false;
    end;
    for i:=1 to n*n do begin
        x:=(i-1) div n+1; y:=i-(x-1)*n;
        if not visit[x,y] then continue;
        if visit[x+1,y] then begin inc(v[i]);map[i,v[i]]:=i+n; end;
        if visit[x-1,y] then begin inc(v[i]);map[i,v[i]]:=i-n; end;
        if visit[x,y+1] then begin inc(v[i]);map[i,v[i]]:=i+1; end;
        if visit[x,y-1] then begin inc(v[i]);map[i,v[i]]:=i-1; end;
    end;
    for i:=1 to n*n do begin
        x:=(i-1) div n+1; y:=i-(x-1)*n;
        if not visit[x,y] then continue;
        fillchar(cover,sizeof(cover),0);
        find(i);
    end;
    for i:=1 to n*n do
        if link[i]<>0 then inc(ans);
    write(ans div 2);
end.
posted @ 2016-10-23 21:25  JRX2015U43  阅读(179)  评论(0编辑  收藏  举报