朝日新聞2004年11月13日のパズルパーク問題

問題

A〜Fの6チームが総当たりの試合をした。勝てば3点,負ければ0,引き分けで1の勝ち点が与えられる。

結果A:11,B:10,C:9,D:8,E:3,F:0の勝ち点になりAが優勝。Aの5試合の結果を求める。

解答への道(ヒント)

問題に関して大きな勘違いをしていた。

Aの試合結果は3勝0敗2分けでしょ。脊髄反射問題じゃんと思った。

しかし問題はAがB,C,D,E,Fと対戦したときの試合結果を求めるということだ。

あー恥ずかしいー。大きな勘違い。

頭で考えると勘違いしやすいのがCの勝ち点9とEの勝ち点3。

プログラムで星取表を虱潰してみると,この2つの勝ち点が2勝0敗3分け,1勝0敗0分けであることが判る。

プログラムの方針は以下の通り。

星取表を配列で作成する。

int star[6][6];  // 星取り表(0:勝ち点0,1:勝ち点1,3:勝ち点3を設定)
//  AT1T2T3T4T5
// A ○○○△△
// T1× a b c d
// T2×    e f g
// T3×      h i
// T4△        j
// T5△

0行目をAチームと仮定し勝ち点を設定する。

  // Aチームの勝ち点は○○○△△と判るので星取り表を埋めてしまう。
  star[0][1] = 3;
  star[0][2] = 3;
  star[0][3] = 3;
  star[0][4] = 1;
  star[0][5] = 1;
  star[1][0] = 0;
  star[2][0] = 0;
  star[3][0] = 0;
  star[4][0] = 1;
  star[5][0] = 1;

a〜jの部分を勝ち点0,勝ち点1,勝ち点3の3通りで虱潰す。

以下のようなコードになる。

  // 星取り表の残りの部分を虱潰す。
  for( int a=0; a< 3; a++) {
    star[1][2] = cnta[a];       // 一方が勝ち点3なら
    star[2][1] = cntb[a];       // 反対側は勝ち点0になる。
    for( int b=0; b< 3; b++) {
      star[1][3] = cnta[b];
      star[3][1] = cntb[b];
      for( int c=0; c< 3; c++) {
        star[1][4] = cnta[c];
        star[4][1] = cntb[c];
        for( int d=0; d< 3; d++) {
          star[1][5] = cnta[d];
          star[5][1] = cntb[d];
          for( int e=0; e< 3; e++) {
            star[2][3] = cnta[e];
            star[3][2] = cntb[e];
            for( int f=0; f< 3; f++) {
              star[2][4] = cnta[f];
              star[4][2] = cntb[f];
              for( int g=0; g< 3; g++) {
                star[2][5] = cnta[g];
                star[5][2] = cntb[g];
                for( int h=0; h< 3; h++) {
                  star[3][4] = cnta[h];
                  star[4][3] = cntb[h];
                  for( int i=0; i< 3; i++) {
                    star[3][5] = cnta[i];
                    star[5][3] = cntb[i];
                    for( int j=0; j< 3; j++) {
                      star[4][5] = cnta[j];
                      star[5][4] = cntb[j];
                      star[][]の各行をチームとみなし,team[0]〜team[5]の勝ち点を計算する。
                   team[]を勝ち点でソートして,勝ち点が11,10,9,8,3,0になっていれば正しい勝ち点の組み合わせが見つかったことになる。
                   重複を除去しないと12件出力されるので,重複を除去する処理を追加すると1件のみ出力される。
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }

cnta[ ],cntb[ ]は勝ち点用の配列。

int cnta[3] = { 0, 1, 3};       // 星取り表行方向の勝ち点可能性
int cntb[3] = { 3, 1, 0};       // 星取り表列方向の勝ち点可能性

team[ ]は構造体にして,チーム名が決まった後にどの行だったかわかるようにする。

struct struct_team {
  int katiten;                  // star[][]に設定した勝ち点の行合計
  int idx;                      // 勝ち点でソートしたとき,star[][]のどの行を指していたかを記録する
}team[6];

勝ち点を計算し問題の条件に合うかどうか比較する部分は以下の通り。

// 各チームの勝ち点を求める。
team[0].katiten = 11;
team[1].katiten = star[1][0] + star[1][1] + star[1][2] + star[1][3] + star[1][4] + star[1][5];
team[2].katiten = star[2][0] + star[2][1] + star[2][2] + star[2][3] + star[2][4] + star[2][5];
team[3].katiten = star[3][0] + star[3][1] + star[3][2] + star[3][3] + star[3][4] + star[3][5];
team[4].katiten = star[4][0] + star[4][1] + star[4][2] + star[4][3] + star[4][4] + star[4][5];
team[5].katiten = star[5][0] + star[5][1] + star[5][2] + star[5][3] + star[5][4] + star[5][5];
team[0].idx = 0; team[1].idx = 1; team[2].idx = 2; team[3].idx = 3; team[4].idx = 4; team[5].idx = 5;
qsort( team, 6, sizeof(struct_team), cmpkatiten);
if( team[0].katiten == 11 && team[1].katiten == 10 && team[2].katiten == 9 && team[3].katiten == 8 && team[4].katiten == 3 && team[5].katiten == 0) {

後は重複を除去して,6x6のループを作成し,出力するだけ。

ps( "  : ABCDEF\n");
for( int x=0; x< 6; x++) {
  ps( "%-.2s : ", "ABCDEF"+x*2);
  for( int y=0; y< 6; y++) {
    if( x == y) ps( " ");
    else ps( "%s", stars(star[team[x].idx][team[y].idx]));
  }
  ps( "\n");
}
ps( "\n");

解速度