朝日新聞2006年1月13日パズル横丁解答

プログラムの実行結果は以下の通り。

2006 x 33032037 = 66262266222
       2006
×  33032037
----------------------
      14042
      6018
    4012
   6018
 6018
6018
----------------------
66262266222

プログラムのソースは以下の通り。

#include "puzutl.h"

void psi( int i) {              // 数字を1件表示する
  if( i< 0 || i > 11) i = 12;
  cstring s = "0123456789 ×?"; // 0〜9まではそのまま表示,10は空白,11が×
  ps( "%-.2s", s+i*2);
}

void psa( int exp[], int w) {   // 数字をw桁表示する
  for( int i=w-1; i >= 0; i--) psi( exp[i]);
  ps( "\n");
}

void ps11() {                   // 11桁の区切り記号のつもり
  for( int i=0; i< 11; i++) {
    ps( "--");
  }
  ps( "\n");
}

void expand( int exp[], int w, int64 val) { // 桁数を指定して1桁ずつ分解する。
  int n = 0;
  while( val) {
    if( n >= w) return;
    exp[n++] = val % 10;        // 下の桁から1桁ずつ求める
    val /= 10;
  }
  while( n < w) {               // 数が足りないときは空白(10)で埋める
    exp[n++] = 10;
  }
}

void checkans( int m, int64 R)  // 掛ける数と結果候補値
{
  int64 x = R;
  while( x) {                   // 結果が2と6だけか?
    if( !((x%10) == 2 || (x%10) == 6)) return; // 2と6以外が含まれる
    x /= 10;
  }
  ps( "2006 x %d = %I64d\n", m, R); // 結果候補として印刷する
  int n = 0;                    // 掛ける数の桁数
  int d[128];                   // 掛ける数を一桁ずつ求める
  for( x=m; x; x/=10) {
    d[n++] = x%10;
  }
  if( !(d[2] == 0 && d[5] == 0)) return; // 式を見ると3桁目と6桁目は0になるはず(実際はこれが無くても答えが1つ)
  int exp[100];
  expand( exp, 11, 2006); psa( exp, 11);
  expand( exp, 11, m); exp[10] = 11/*×*/; psa( exp, 11);
  ps11();
  for( int j=0; j< n; j++) {    // 2006 x 1桁ずつの掛け算
    if( d[j]) {
      expand( exp, 11-j, 2006 * d[j]); psa( exp, 11-j);
    }
  }
  ps11();
  expand( exp, 11, R); psa( exp, 11);
}

int main( int argc, cstring argv[])
{
#if 0
  // めちゃくちゃ時間が掛かる方法
  int i, j;
  int64 r;
  ProcTime pt; pt.start();
  for( i=20000000; i< 100000000; i++) {
    YesNo ynNG = NO;
    for( int j=i; j; j/=10) if( (j%10) == 1) { ynNG = YES; break;} // 掛ける数に1を含んでいたらダメ
    if( ynNG) continue;
    r = (int64)2006 * i;
    checkans( i, r);
  }
  pt.end();
  ps( "%g 秒\n", pt.sec());
#else
  // 直ぐに結果が表示される方法
  int no[] = { 2, 6};           // 結果は2と6だけ
  for( int a=0; a< 2; a++) {
    for( int b=0; b< 2; b++) {
      for( int c=0; c< 2; c++) {
        for( int d=0; d< 2; d++) {
          for( int e=0; e< 2; e++) {
            for( int f=0; f< 2; f++) {
              for( int g=0; g< 2; g++) {
                for( int h=0; h< 2; h++) {
                  for( int i=0; i< 2; i++) {
                    for( int j=0; j< 2; j++) {
                      for( int k=0; k< 2; k++) {
                        int64 R = no[k]+10*(no[j]+10*(no[i]+10*(no[h]+10*(no[g]+10*(no[f]+10*(no[e]+10*(no[d]+10*(no[c]+10*(no[b]+10*(no[a]*(int64)1))))))))));
                        if( (R % 2006) == 0) {
                          int m = R/2006;
                          YesNo ynNG = NO;
                          for( int j=m; j; j/=10) if( (j%10) == 1) { ynNG = YES; break;} // 掛ける数に1を含んでいたらダメ
                          if( ynNG) continue;
                          checkans( m, R);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
#endif
  return    0;
}