【二分图匹配】宫廷守卫 PASCAL

宫廷守卫【二分图匹配】

Time Limit:10000MS  Memory Limit:65536K
Total Submit:50 Accepted:8

Description

  从前有一个王国,这个王国的城堡是一个矩形,被分为M×N个方格。一些方格是墙,而另一些是空地。这个王国的国王在城堡里设了一些陷阱,每个陷阱占据一块空地。
  一天,国王决定在城堡里布置守卫,他希望安排尽量多的守卫。守卫们都是经过严格训练的,所以一旦他们发现同行或同列中有人的话,他们立即向那人射击。因此,国王希望能够合理地布置守卫,使他们互相之间不能看见,这样他们就不可能互相射击了。守卫们只能被布置在空地上,不能被布置在陷阱或墙上,且一块空地只能布置一个守卫。如果两个守卫在同一行或同一列,并且他们之间没有墙的话,他们就能互相看见。(守卫就像象棋里的车一样)
  你的任务是写一个程序,根据给定的城堡,计算最多可布置多少个守卫,并设计出布置的方案。

Input

  第一行两个整数M和N(1≤M,N≤200),表示城堡的规模。
  接下来M行N列的整数,描述的是城堡的地形。第i行j列的数用ai,j表示。
  ai,j=0,表示方格[i,j]是一块空地;
  ai,j=1,表示方格[i,j]是一个陷阱;
  ai,j=2,表示方格[i,j]是墙。

Output

  第一行一个整数K,表示最多可布置K个守卫。

Sample Input

3 4                                                 
2 0 0 0                                         
2 2 2 1                                         
0 1 0 2

  样例数据如图5-2(黑色方格为墙,白色方格为空地,圆圈为陷阱,G表示守卫)

【二分图匹配】宫廷守卫 <wbr>PASCAL

Sample Output

2

Hint

【算法分析】
  本题的关键在构图。
  城堡其实就是一个棋盘。我们把棋盘上横向和纵向连续的极长段(不含墙)都分离出来。显然,每一段上最多只能放一个guard,而且guard总是放在一个纵向段和一个横向段的交界处,所以一个guard和一个纵向段和一个横向段有关。
  我们把纵向段和横向段都抽象成图中的节点,如果一个纵向段和一个横向段相交的话,就在两点之间连一条边。这样,guard就成为了图中的边。前面得出的性质抽象成图的语言就是,每个点只能和一条边相连,每条边只能连接一个纵向边的点和一个横向边的点。因此,这样的图是二分图,我们所求的正是二分图的匹配。而要布置最多的guards,就是匹配数要最大,即最大匹配。
  图中节点数为n(n≤200),求最大匹配的时间复杂度为O(n^2.5)。

Source

 

这是一道二分图的题。

一开始真没看出来,建图方法真是奇葩。

而且数据也很奇葩:

 

var
 m,n,sh,sz,s:longint;
 a:array[1..200+1,1..200+1]of longint;
 h,z:array[1..200+1,1..200+1]of longint;
 map:array[1..7000+1,1..7000+1]of boolean;//这里要开到7000,提交了好多遍才过
 mark:array[1..7000+1]of boolean;
 link:array[1..7000+1]of longint;
procedure init;
var
 i,j:longint;
begin
 read(m,n);
 for i:=1 to m do
  for j:=1 to n do
   read(a[i,j]);
 for i:=1 to m do a[i,n+1]:=2;
 for i:=1 to n do a[m+1,i]:=2;
end;
procedure work;
var
 i,j,x:longint;
begin
 sh:=0;
 sz:=0;
 for i:=1 to m do
  for j:=1 to n do
   if (a[i,j]<>2) and (a[i,j]<>1) then
   begin
   if h[i,j]=0 then
    begin
    inc(sh);
    x:=i;
    while a[x,j]<>2 do
     begin
     h[x,j]:=sh;
     inc(x);
     end;
    end;
   if z[i,j]=0 then
    begin
    inc(sz);
    x:=j;
    while a[i,x]<>2 do
     begin
     z[i,x]:=sz;
     inc(x);
     end;
    end;
   end;
  for i:=1 to m do
   for j:=1 to n do
    if (h[i,j]<>0)and(z[i,j]<>0)and(a[i,j]=0) then
     map[h[i,j],z[i,j]]:=true;
end;
function find(x:longint):boolean;
var
 i,q:longint;
begin
 for i:=1 to sz do
  if map[x,i] and not mark[i] then
  begin
  q:=link[i]; link[i]:=x; mark[i]:=true;
  if (q=0)or find(q) then exit(true);
  link[i]:=q;
  end;
 exit(false);
end;
procedure main;
var
 i:longint;
begin
 s:=0;
 for i:=1 to sh do
  begin
   fillchar(mark,sizeof(mark),0);
   if find(i) then inc(s);
  end;
end;
begin
 assign(input,'data.txt');
 reset(input);
 init;
 work;
 main;
 writeln(s);
 close(input);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值