二分匹配的灵活运用
3041还是比较好想的,考虑到横排/竖排射一枪就能搞定这一行/一列的所有点,
我们以行数为点集x,列数为点集y,在目标点(xi,yi)之间连一条边
这样最小射击次数=最小点覆盖(边两端点至少有一个点在覆盖集中)=最大匹配
poj2226是它的加强版
这里一块木板不能覆盖非泥泞点,也就是说一块木板不一定能搞定那一行所有的点
于是我们考虑到将连续的泥泞点标号,表示这些泥泞点是由哪个木板盖住(横竖分别标号)
这样,我们以横排木板为点集x,竖排木板为点集y,对于每个点,在盖住它的横竖排木板之间连边
(显然,每个点至多被一个横木板,一个竖木板覆盖)
于是根据poj3041,做匈牙利即可
1 var a:array[0..60,0..60] of char; 2 h1,h2:array[0..60,0..60] of longint; 3 map:array[0..2000,0..2000] of boolean; 4 cx,cy:array[0..2000] of longint; 5 v:array[0..2000] of boolean; 6 s1,s2,ans,i,j,n,m:longint; 7 8 function find(x:longint):longint; //增广路 9 var i:longint; 10 begin 11 for i:=1 to s2 do 12 if map[x,i] and not v[i] then 13 begin 14 v[i]:=true; 15 if (cy[i]=-1) or (find(cy[i])>0) then 16 begin 17 cx[x]:=i; 18 cy[i]:=x; 19 exit(1); 20 end; 21 end; 22 exit(0); 23 end; 24 25 begin 26 readln(n,m); 27 for i:=1 to n do 28 begin 29 for j:=1 to m do 30 begin 31 read(a[i,j]); 32 end; 33 readln; 34 end; 35 for i:=1 to n do //横排标号 36 begin 37 j:=1; 38 while j<=m do 39 begin 40 if a[i,j]='*' then 41 begin 42 inc(s1); 43 h1[i,j]:=s1; 44 inc(j); 45 while a[i,j]='*' do 46 begin 47 h1[i,j]:=s1; 48 inc(j); 49 end; 50 end 51 else inc(j); 52 end; 53 end; 54 for i:=1 to m do //竖排标号 55 begin 56 j:=1; 57 while j<=n do 58 begin 59 if a[j,i]='*' then 60 begin 61 inc(s2); 62 h2[j,i]:=s2; 63 inc(j); 64 while a[j,i]='*' do 65 begin 66 h2[j,i]:=s2; 67 inc(j); 68 end; 69 end 70 else inc(j); 71 end; 72 end; 73 for i:=1 to n do 74 for j:=1 to m do 75 map[h1[i,j],h2[i,j]]:=true; //连边 76 fillchar(cx,sizeof(cx),255); 77 fillchar(cy,sizeof(cy),255); 78 for i:=1 to s1 do //匈牙利 79 if cx[i]=-1 then 80 begin 81 fillchar(v,sizeof(v),false); 82 ans:=ans+find(i); 83 end; 84 writeln(ans); 85 end.
一定要注意问题建模的灵活运用;