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

 

你可能感兴趣的:(poj)