射击比赛 (POJ 1719) 题解
【问题描述】
我们假设射击的目标是一个由R*C(2≤R≤C≤ 1000)个小方格组成的矩形网格。网格中每一列恰有2个白色的小方格和R-2个黑色的小方格。定义网格的行从顶至底编号为1~R,列从左至右编号为1~C。 射击者可射击C次。在连续的C次射击中,若每列恰好有一个白色的方格被射中,且不存在无白色方格被射中的行,这样的射击才是正确的。现给出N组数据,对于每组数据,如果存在正确的射击方法,则要求找到它,若不存在,输出NO。
【样例输入】
2
4 4
2 4
3 4
1 3
1 4
5 5
1 5
2 4
3 4
2 4
2 3
【样例输出】
2 3 1 4
NO
【解题思路】
本题为1997年CEOI最后一题,解题的思路主要在于贪心策略与贪心的证明。
贪心策略:
1、统计所有行包含的白格数。
2、从还没有射击格的行中选出一个白格数最少的。
3、检查所选的行 (1)若所选行的白格数为0,则输出无解; (2)否则从所选行的白格中任选一个作为射击格,并将与该格同列的另一 个白格所处行的白格数减1。
4、返回到第2步,直到所有的行都有射击格。
5、若还有列没有选射击格,则在该列任选一白格作为射击格即可。
贪心证明:
用h[i]表示第i行的白格数。如果最开始的时候: ①min{h[i]}=0:第i行已经没有办法找到可作为射击格的白格,那么问题只能无解。 ②min{h[i]}=1:那么第i行的这一个白格必须要作为射击格,否则将因第i行没有射击格而造成问题无解。
③min{h[i]}≥2:那么在这一 行任选一个白格,顶多只会造成剩余行中有一行h值为1,再处理那一行,最多也只会再造成剩余行中有一行h 值为1,如此往复,将保持h值为1的行数不超过1行,最后最坏的情况也是造成最后一行的h值为1,继续下去所有行就都已选取了射击格了。因此,如果原问题有解,该贪心方法一定能找到一种正确的方案。由此可以证明,此贪心方法是正确的。确定贪心标准。
【代码实现】
1 var a:array[1..1000] of longint; 2 b:array[1..1000,1..1000] of boolean; 3 fr,fc:array[1..1000] of boolean; 4 ans:array[1..1000] of longint; 5 n,i,j,r,c,x1,x2,k,pos,min,code,q:longint; 6 flag:boolean; 7 begin 8 readln(code); 9 for q:=1 to code do 10 begin 11 fillchar(fr,sizeof(fr),false); 12 fillchar(fc,sizeof(fc),false); 13 fillchar(a,sizeof(a),0); 14 fillchar(b,sizeof(b),0);//注意初始化,没初始化WA几次…… 15 readln(r,c); 16 for i:=1 to c do 17 begin 18 readln(x1,x2); 19 b[x1,i]:=true;b[x2,i]:=true; 20 inc(a[x1]);inc(a[x2]); 21 end; 22 repeat 23 min:=maxlongint; 24 flag:=true; 25 for i:=1 to r do 26 if not(fr[i]) then 27 break; 28 if not(fr[i]) then 29 flag:=false; 30 if flag then break; 31 for i:=1 to r do 32 if (a[i]<min)and(not(fr[i])) then 33 begin 34 min:=a[i]; 35 pos:=i; 36 end; 37 if a[pos]=0 then 38 begin 39 writeln('NO'); 40 break; 41 end; 42 fr[pos]:=true; 43 for j:=1 to c do 44 if (b[pos,j])and(not(fc[j])) then 45 begin 46 ans[j]:=pos;fc[j]:=true; 47 for k:=1 to r do 48 if (b[k,j])and(k<>pos) then 49 dec(a[k]); 50 break; 51 end; 52 until flag; 53 if a[pos]=0 then 54 continue; 55 for i:=1 to c do 56 if ans[i]=0 then 57 for j:=1 to r do 58 if b[j,i] then 59 begin 60 ans[i]:=j; 61 break; 62 end; 63 write(ans[1]); 64 for i:=2 to c do 65 write(' ',ans[i]); 66 writeln; 67 end; 68 end.