射击比赛 (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.
View Code

原文地址:https://www.cnblogs.com/PengBoLiuXu/p/4486120.html