Copyright (C) IOI日本委員会 1995. All rights reserved.


問題3の解答と解説

 下記の例解プログラムの関数 solvable は、解が存在するなら その一つを求めるとともに、解が存在するか否かを値とする論理関数である。
 変数 level の値が1から nm-1 まで動くとき、 I = (levelをnで割った商) + 1、J = (levelをnで割った余り) + 1 とすると、 (I,J) は 1≦i≦m, 1≦j≦n なる全範囲を動く。 いま、plan[i][j] の値を設定する範囲を (i=1〜I-1, j=1〜n) および (i=I, j=1〜J-1) に制限する(すなわち、m行n列で 構成される解の第1行から第I-1行までと第I行の第1列から第J-1列まで の要素以外はすべて0とする)と解が存在しないとする。そこで、 この範囲を (i,j)=(I,J) 以降にまで広げて解があるか 否かを全探索を行なって調べるのが関数 solvable である。

 この問題では上記のような素朴な方法(この方法は問題のサイズ m,n が ちょっと大きくなるとすぐ破綻してしまう)より以上のことは期待していない が、ネットワ−クフロ−の理論で知られているアルゴリズムを用いると プログラムは多少複雑になるがもっと効率よく解くことができる。


Cプログラム(1)

boolean solvable(int level)
{
   int i,j,k,min; boolean flag;

   if (level==n*m) return false;
   i = level/n+1; j = level%n+1;
   if (plan[i][0]>0 && plan[0][j]>0) {
      min = x;
      if (min>plan[i][0]) min = plan[i][0];
      if (min>plan[0][j]) min = plan[0][j];
      plan[i][j] = min;
      plan[i][0] = plan[i][0]-min;
      plan[0][j] = plan[0][j]-min;
      flag = true;
      for (k=1; k<=n; k++) if (plan[0][k]!=0) flag = false;
      if (flag) return true;
      for (k=1; k<=min; k++) {
         if (solvable(level+1)) return true;
         plan[i][j] = plan[i][j]-1;
         plan[i][0] = plan[i][0]+1;
         plan[0][j] = plan[0][j]+1;
      }
   }
   else return solvable(level+1);
   return false;
}

void main()
{
   前略
   if (solvable(0)) ...
   後略
}


BASICプログラム(1)

   前略
   IF solvable(0) THEN ...
   後略
END

FUNCTION solvable (level AS INTEGER)
   solvable = false
   IF level = n * m THEN EXIT FUNCTION
   i = level \ n + 1: j = level MOD n + 1
   IF (plan(i, 0) > 0) AND (plan(0, j) > 0) THEN
      min = x
      IF min > plan(i, 0) THEN min = plan(i, 0)
      IF min > plan(0, j) THEN min = plan(0, j)
      plan(i, j) = min
      plan(i, 0) = plan(i, 0) - min
      plan(0, j) = plan(0, j) - min
      flag = true
      FOR k = 1 TO n
         IF plan(0, k) <> 0 THEN flag = false
      NEXT k
      IF flag THEN solvable = true: EXIT FUNCTION
      FOR k = 1 TO min
         IF solvable(level + 1) THEN solvable = true: EXIT FUNCTION
         plan(i, j) = plan(i, j) - 1
         plan(i, 0) = plan(i, 0) + 1
         plan(0, j) = plan(0, j) + 1
      NEXT k
   ELSE solvable = solvable(level + 1)
   END IF
END FUNCTION


Pascalプログラム(1)

   function solvable(level:integer):boolean;
      var i,j,k,min:integer; flag:boolean;
   begin
      solvable:=false;
      if level=n*m then exit;
      i:=level div n + 1; j:=level mod n + 1;
      if (plan[i][0]>0) and (plan[0][j]>0) then begin
         min:=x;
         if min>plan[i][0] then min:=plan[i][0];
         if min>plan[0][j] then min:=plan[0][j];
         plan[i][j]:=min;
         plan[i][0]:=plan[i][0]-min;
         plan[0][j]:=plan[0][j]-min;
         flag:=true;
         for k:=1 to n do if plan[0][k]<>0 then flag:=false;
         if flag then begin solvable:=true; exit; end;
         for k:=1 to min do begin
            if solvable(level+1) then begin solvable:=true; exit; end;
            plan[i][j]:=plan[i][j]-1;
            plan[i][0]:=plan[i][0]+1;
            plan[0][j]:=plan[0][j]+1;
         end;
      end else solvable:=solvable(level+1);
   end;

begin { main }
   前略
   if solvable(0) then ...
   後略
end.


JOIホームページへ戻る

JOI'95へ戻る