横丁の名探偵

トップ > Cマガ電脳クラブ挑戦記録の部屋 > Cマガ電脳クラブソースリスト(2004年分)



第164回(11月)年子平方数

/******************************************************************************
Cマガジン(2004年8月)電脳クラブ出題(年子平方数)
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define OFF 0        /* オフ /*
#define ON  1        /* オン /*
#define N0 0         /* 基準の0 /*
#define N1 1         /* 単位の数 /*
#define N2 2         /* N1 + N1 /*
#define N3 3         /* 平方数の元の数のロング数の桁数 /*
#define N5 5         /* 平方数のロング数の桁数 /*
#define N9 9         /* N10 - 1 /*
#define N10 10       /* 32bitで表現できる桁数 /*
#define N20 20       /* 平方数表示文字数の最大値 /*
#define N21 21       /* 求める解の数 /*
#define K0 N0        /* 0 /*
#define K1 1         /* NO + N1 = 1 /*
#define K2 2         /* K1 + N1 = 2 /*
#define K3 3         /* K2 + N1 = 3 /*
#define K4 4         /* K3 + N1 = 4 /*
#define K5 5         /* K4 + N1 = 5 /*
#define K6 6         /* K5 + N1 = 6 /*
#define K7 7         /* K6 + N1 = 7 /*
#define K8 8         /* K7 + N1 = 8 /*
#define K9 9         /* K8 + N1 = 9 /*
#define M1 10        /* 10進数の10 /*
#define M2 100       /* M1 * M1 /*
#define M3 1000      /* M2 * M1 /*
#define M4 10000     /* ロング数の最大値+1 /*
#define M5 100000    /* M4 * M1 /*
#define M7 10000000  /* M5 * M2 /*
#define M8 100000000 /* M4 * M4 /*
#define L21 21       /* N20 + N1(NULLターミネート分) /*
#define CSPACE ' '   /* スペース /*
#define CZERO '0'    /* ゼロ /*
#define FORM1 "%4d%4d%4d%4d%4d" /* ロング数→文字列用書式 /*
#define FORM2 "%2d %10d %20s\n" /* 解答表示用書式 /*
int count = 0; /* 正解の数(21まで) /*
/******************************************************************************
機能:ロング数を2乗する。 a1 = b1 * b1
引数:
  (1) a1 : 2乗後の数(ロング数)
  (2) b1 : 2乗される数(通常数)
戻り値:なし
*****************************************************************************/*
void multi(int a1[] , int b1)
{
    int i , j; /* ループの制御変数 /*
    int b2[N3]; /* 2乗される数のロング数 /*
    b2[N2] = b1 / M8; /* 2乗される数をロング数に変換する。一億、十億の位 /*
    b2[N1] = (b1 / M4) % M4; /* 万〜千万の位 /*
    b2[N0] = b1 % M4; /* 一〜千の位 /*
    for(i = N0 ; i < N5 ; i++) a1[i] = N0; /* 初期化 /*
    for(i = N0 ; i < N3 ; i++) {
        for(j = N0 ; j < N3 ; j++) {
            a1[i + j] += b2[i] * b2[j]; /* 各桁ごとの掛け算 /*
        }
    }
    for(i = N0 ; i < N5 - N1 ; i++) {
        a1[i + N1] += a1[i] / M4; /* 桁あふれ処理 /*
        a1[i] %= M4;
    }
}
/******************************************************************************
機能:平方数を求め、題意に合うかチェックし、合うなら表示する。
引数:
  (1) i  : 2乗される前の数
  (2) m1 : iの桁数
戻り値:なし
*****************************************************************************/*
void sub(int i , int m1)
{
    int j; /* ループの制御変数 /*
    int a1[N5]; /* 平方数のロング数 /*
    int n1 , n2; /* 平方数の左半分、右半分 /*
    int f1 = OFF; /* ゼロサプレス用フラグ OFF:数字未登場、ON:登場済み/*
    char buf[L21]; /* 平方数表示用 /*
    multi(a1 , i); /* 平方数を求める /*
    if     (m1 == K1) { /* 平方前の数が1桁の場合 /*
        n1 = a1[K0] / M1; /* 平方数の左側半分 /*
        n2 = a1[K0] % M1; /* 平方数の右側半分 /*
    }
    else if(m1 == K2) { /* 同2桁の場合 /*
        n1 = a1[K0] / M2; /* 平方数の左側半分 /*
        n2 = a1[K0] % M2; /* 平方数の右側半分 /*
    }
    else if(m1 == K3) { /* 同3桁の場合 /*
        n1 = a1[K1] * M1 + a1[K0] / M3; /* 平方数の左側半分 /*
        n2 = a1[K0] % M3; /* 平方数の右側半分 /*
    }
    else if(m1 == K4) { /* 同4桁の場合 /*
        n1 = a1[K1]; /* 平方数の左側半分 /*
        n2 = a1[K0]; /* 平方数の右側半分 /*
    }
    else if(m1 == K5) { /* 同5桁の場合 /*
        n1 = a1[K2] * M3 + a1[K1] / M1; /* 平方数の左側半分 /*
        n2 = (a1[K1] % M1) * M4 + a1[K0]; /* 平方数の右側半分 /*
    }
    else if(m1 == K6) { /* 同6桁の場合 /*
        n1 = a1[K2] * M2 + a1[K1] / M2; /* 平方数の左側半分 /*
        n2 = (a1[K1] % M2) * M4 + a1[K0]; /* 平方数の右側半分 /*
    }
    else if(m1 == K7) { /* 同7桁の場合 /*
        n1 = a1[K3] * M5 + a1[K2] * M1 + a1[K1] / M3; /* 平方数の左側半分 /*
        n2 = (a1[K1] % M3) * M4 + a1[K0]; /* 平方数の右側半分 /*
    }
    else if(m1 == K8) { /* 同8桁の場合 /*
        n1 = a1[K3] * M4 + a1[K2]; /* 平方数の左側半分 /*
        n2 = a1[K1] * M4 + a1[K0]; /* 平方数の右側半分 /*
    }
    else if(m1 == K9) { /* 同9桁の場合 /*
        n1 = a1[K4] * M7 + a1[K3] * M3 + a1[K2] / M1; /* 平方数の左側半分 /*
        n2 = (a1[K2] % M1) * M8 + a1[K1] * M4 + a1[K0]; /* 平方数の右側半分 /*
    }
    if(n1 + N1 != n2 && n1 != n2 + N1) return; /* ±1チェック /*
    count++;
    sprintf(buf , FORM1 , a1[K4] , a1[K3] , a1[K2] , a1[K1] , a1[K0]);
    for(j = N0 ; j < N20 ; j++) { /* 平方数の先行ゼロサプレス /*
        if(f1 == OFF) { /* 数字未登場 /*
            if     (buf[j] == CZERO)  buf[j] = CSPACE;
            else if(buf[j] != CSPACE) f1 = ON;
        }
        else { /* 数字登場済み /*
            if(buf[j] == CSPACE) buf[j] = CZERO;
        }
    }
    printf(FORM2 , count , i , buf); /* 解答表示 /*
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:EXIT_SUCCESS
*****************************************************************************/*
int main(void)
{
    double d1; /* 3.16 ... = sqrt(10.0) /*
    int s1; /* 2乗して偶数桁になる開始の数 /*
    int i , j; /* ループの制御変数 /*
    int z1 = N1; /* 1 , 10 , 100 , 1000 , ... /*
    d1 = sqrt((double)M1);
    for(i = N0 ; i < N9 ; i++) {
        s1 = (int)(d1 * (double)z1) + N1; /* 開始の数を求める /*
        z1 *= M1;
        for(j = s1 ; j < z1 ; j++) {
            sub(j , i + N1);
            if(count == N21) break; /* 21個もとまったら終了 /*
        }
    }
    return(EXIT_SUCCESS);
}

/******************************************************************************
Cマガジン2004年8月号電脳クラブ出題(偶数桁の回文平方数)
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define OFF 0     /* オフ /*
#define ON  1     /* オン /*
#define N0 0      /* 基準の0 /*
#define N1 1      /* 単位の数 /*
#define N2 2      /* N1 + N1 /*
#define N3 3      /* 平方数の元の数のロング数の桁数 /*
#define N6 6      /* 平方数のロング数の桁数 /*
#define N9 9      /* N10 - 1 /*
#define N10 10    /* 32bitで表現できる桁数 /*
#define N24 24    /* 平方数表示文字数の最大値 /*
#define N4 4      /* 求める解の数 /*
#define M0 1      /* = N1 /*
#define M1 10     /* 10進数の10 /*
#define M2 100    /* M1 * M1 /*
#define M3 1000       /* M2 * M1 /*
#define M4 10000      /* ロング数の最大値+1 /*
#define KETA04  4 /* ロング数の桁数 /*
#define L25 25    /* N20 + N1(NULLターミネート分) /*
#define CSPACE ' '    /* スペース /*
#define CZERO '0'     /* ゼロ /*
typedef struct {
    int idx; /* 索引 /*
    int val; /* 値 /*
} RIGHT;
int count = 0; /* 求まった解の数 /*
/******************************************************************************
機能:ロング数を2乗する。 a1 = b1 * b1
引数:
  (1) a1 : 2乗後の数(ロング数)
  (2) b1 : 2乗される数(通常数)
戻り値:なし
*****************************************************************************/*
void multi(int a1[N6] , int b2[N3])
{
    int i , j;
    for(i = N0 ; i < N6 ; i++) a1[i] = N0;
    for(i = N0 ; i < N3 ; i++) {
        for(j = N0 ; j < N3 ; j++) {
            a1[i + j] += b2[i] * b2[j];
        }
    }
    for(i = N0 ; i < N6 - N1 ; i++) { /* 桁あふれ処理 /*
        a1[i + N1] += a1[i] / M4;
        a1[i] %= M4;
    }
}
/******************************************************************************
機能:平方して、回文がどうかチェックする。
引数:
  (1) b1 :平方前の数(ロング数)
  (2) m1 : 平方前の桁数
戻り値:なし
*****************************************************************************/*
void sub(int b1[] , int m1)
{
    int j , n = N0;
    int a1[N6]; /* 平方数用ロング数 /*
    int f1 = OFF;
    char buf[L25]; /* 平方数表示用 /*
    static int p1[] = {M0 , M1 , M2 , M3}; /* ロング数から1桁得る /*
    int j2;
    multi(a1 , b1);
    for(j = 0 ; j < m1 ; j++) { /* 回文かどうかのチェック /*
        j2 = m1 * 2 - j - 1;
        if((a1[j / KETA04] / p1[j % KETA04]) % M1
            != (a1[j2 / KETA04] / p1[j2 % KETA04]) % M1) return;
    }
    count++;
    printf("%2d " , count);
    sprintf(buf , "%4d%4d%4d" , b1[2] , b1[1] , b1[0]);
    for(j = N0 ; j < 12 ; j++) { /* 平方数の先行ゼロサプレス /*
        if(f1 == OFF) { /* 数字未登場 /*
            if     (buf[j] == CZERO)  buf[j] = CSPACE;
            else if(buf[j] != CSPACE) f1 = ON;
        }
        else { /* 数字登場済み /*
            if(buf[j] == CSPACE) buf[j] = CZERO;
        }
    }
    printf("%12s " , buf);
    f1 = OFF;
    sprintf(buf , "%4d%4d%4d%4d%4d%4d" ,
    a1[5] , a1[4] , a1[3] , a1[2] , a1[1] , a1[0]);
    for(j = N0 ; j < N24 ; j++) { /* 平方数の先行ゼロサプレス /*
        if(f1 == OFF) { /* 数字未登場 /*
            if     (buf[j] == CZERO)  buf[j] = CSPACE;
            else if(buf[j] != CSPACE) f1 = ON;
        }
        else { /* 数字登場済み /*
            if(buf[j] == CSPACE) buf[j] = CZERO;
        }
    }
    printf("%24s\n" , buf);
}
/******************************************************************************
機能:上位、下位から平方前のロング数を組み立てる。
引数:
  (1) joi : 上位の桁(2〜4桁)
  (2) kai : 下位の桁(2〜4桁)
  (3) m1  : 平方前の桁数
戻り値:
*****************************************************************************/*
void makeLong(int joi , int kai , int m1)
{
    int i; /* ループ制御変数 /*
    int b1[N3]; /* 2乗前の数 /*
    b1[0] = kai;
    b1[2] = N0;
    if     (m1 <= 4) { /* 2、3、4桁 /*
        if(joi != kai) return;
        b1[1] = N0;
        sub(b1 , m1);
    }
    else if(m1 == 5) { /* 5桁 /*
        if(joi % M1 != kai / M2) return;
        b1[0] += ((joi / M1) % M1) * M3;
        b1[1] = joi / M2;
        sub(b1 , m1);
    }
    else if(m1 == 6) { /* 6桁 /*
        b1[0] += (joi % M1) * M3;
        b1[1] = joi / M1;
        sub(b1 , m1);
    }
    else if(m1 == 7) { /* 7桁 /*
        if(joi % 10 != kai / M3) return;
        b1[1] = joi / M1;
        sub(b1 , m1);
    }
    else if(m1 == 8) { /* 8桁 /*
        b1[1] = joi;
        sub(b1 , m1);
    }
    else if(m1 == 9) { /* 9桁 /*
        b1[1] = (joi % M3) * M1;
        b1[2] = joi / M3;
        for(i = N0 ; i < M1 ; i++) { /* 上位と下位の間の数 /*
            b1[1]++;
            sub(b1 , m1);
            if(count == N4) return; /* 答えが4個求まった /*
        }
    }
    else if(m1 == 10) { /* 10桁 /*
        b1[1] = (joi % M2) * M2;
        b1[2] = joi / M2;
        for(i = N0 ; i < M2 ; i++) { /* 上位と下位の間の数 /*
            b1[1]++;
            sub(b1 , m1);
            if(count == N4) return; /* 答えが4個求まった /*
        }
    }
    else if(m1 == 11) { /* 11桁 /*
        b1[1] = (joi % M1) * M3;
        b1[2] = joi / M1;
        for(i = N0 ; i < M3 ; i++) { /* 上位と下位の間の数 /*
            b1[1]++;
            sub(b1 , m1);
            if(count == N4) return; /* 答えが4個求まった /*
        }
    }
    else if(m1 == 12) { /* 12桁 /*
        b1[1] = N0;
        b1[2] = joi;
        for(i = N0 ; i < M4 ; i++) { /* 上位と下位の間の数 /*
            b1[1]++;
            sub(b1 , m1);
            if(count == N4) return; /* 答えが4個求まった /*
        }
    }
}
/******************************************************************************
機能:qsort用cmp関数
引数:a,b
戻り値:-1,0,1
*****************************************************************************/*
int cmp(const void *a , const void *b)
{
    RIGHT *za , *zb;
    za = (RIGHT *)a;
    zb = (RIGHT *)b;
    if     (za->val <  zb->val) return(-1);
    else if(za->val == zb->val) return(0);
    else                        return(1);
}
/******************************************************************************
機能:メイン補助
引数:なし
戻り値:なし
*****************************************************************************/*
void main2(void)
{
    int i0 , i1 , i2 , i3; /* ループ制御変数 /*
    int min , max; /* 2乗後の最小、最大 /*
    RIGHT right[M4]; /* 2乗後の右半分 /*
    int rev; /* 2乗後の左側の反転=右側 /*
    double d1; /* sqrt(10.0) = 3.16... /*
    int s1;
    int t2 , t3;
    d1 = sqrt((double)M1);
/* 2桁(2乗前)→4桁(平方数) /*
    for(i1 = N0 ; i1 < M2 ; i1++) {
        right[i1].idx = i1;
        right[i1].val = (i1 * i1) % M2;
    }
    qsort((void *)right , M2 , sizeof(RIGHT) , cmp);
    s1 = (int)(d1 * (double)M1);
    for(i1 = s1 ; i1 < M2 ; i1++) {
        min = i1 * i1 / M2; /* 2乗後の左半分最小値 /*
        max = (i1 + 1) * (i1 + 1) / M2; /* 2乗後の左半分最大値 /*
        for(i2 = min ; i2 <= max ; i2++) {
            rev = i2 / M1 + (i2 % M1) * M1;
            t2 = N0;
            t3 = M2 / 2;
            while(t3 > N0) { /* 2分岐探査 /*
                if(right[t2 + t3].val < rev) t2 += t3;
                t3 /= 2;
            }
            for(i3 = N0 ; i3 < M2 ; i3++) {
                if(rev == right[i3].val) makeLong(i1 , right[i3].idx , 2);
                if(right[i3].val > rev) break;
                if(count == N4) return; /* 答えが4個求まった /*
            }
        }
    }
/* 3桁〜6桁→6桁〜12桁(平方数) /*
    for(i1 = N0 ; i1 < M3 ; i1++) {
        right[i1].idx = i1;
        right[i1].val = (i1 * i1) % M3;
    }
    qsort((void *)right , M3 , sizeof(RIGHT) , cmp);
    s1 = (int)(d1 * (double)M2);
    for(i0 = 3 ; i0 < 7 ; i0++) {
        for(i1 = s1 ; i1 < M3 ; i1++) {
            min = i1 * i1 / M3; /* 2乗後の左半分最小値 /*
            max = (i1 + N1) * (i1 + N1) / M3; /* 2乗後の左半分最大値 /*
            for(i2 = min ; i2 <= max ; i2++) {
                rev = i2 / M2 + ((i2 / M1) % M1) * M1 + (i2 % M1) * M2;
                t2 = N0;
                t3 = M3 / 2;
                while(t3 > 0) { /* 2分岐探査 /*
                    if(right[t2 + t3].val < rev) t2 += t3;
                    t3 /= 2;
                }
                for(i3 = t2 ; i3 < M3 ; i3++) {
                    if(rev == right[i3].val) makeLong(i1 , right[i3].idx , i0);
                    if(right[i3].val > rev) break;
                    if(count == N4) return; /* 答えが4個求まった /*
                }
            }
        }
    }
/* 7桁〜12桁→14桁〜24桁(平方数) /*
    for(i1 = N0 ; i1 < M4 ; i1++) {
        right[i1].idx = i1;
        right[i1].val = (i1 * i1) % M4;
    }
    qsort((void *)right , M4 , sizeof(RIGHT) , cmp);
    s1 = (int)(d1 * (double)M3);
    for(i0 = 7 ; i0 < 13 ; i0++) {
        for(i1 = s1 ; i1 < M4 ; i1++) {
            min = i1 * i1 / M4; /* 2乗後の左半分最小値 /*
            max = (i1 + N1) * (i1 + N1) / M4; /* 2乗後の左半分最大値 /*
            for(i2 = min ; i2 <= max ; i2++) {
                rev = i2 / M3 + ((i2 / M2) % M1) * M1 + ((i2 / M1) % M1) * M2
                   + (i2 % M1) * M3;
                t2 = N0;
                t3 = M4 / 2;
                while(t3 > N0) { /* 2分岐探査 /*
                    if(right[t2 + t3].val < rev) t2 += t3;
                    t3 /= 2;
                }
                for(i3 = t2 ; i3 < M4 ; i3++) {
                    if(rev == right[i3].val) makeLong(i1 , right[i3].idx , i0);
                    if(right[i3].val > rev) break;
                    if(count == N4) return; /* 答えが4個求まった /*
                }
            }
        }
    }
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:EXIT_SUCCESS
*****************************************************************************/*
int main(void)
{
    main2();
    return(EXIT_SUCCESS);
}


第162回(9月)切手切って乗りきって

/******************************************************************************
Cマガジン2004年6月号出題(切手切って乗りきって)
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#define OFF 0    /* オフ /*
#define ON  1    /* オン /*
#define Z3 3     /* 分割後の切手片の1辺の長さ /*
#define Z4 4     /* 分割後の切手片の1辺の長さ /*
#define Z5 5     /* 分割前の切手片の1辺の長さ /*
#define Z7 7     /* Z4 * 2 - N1 中心から正負それぞれに向かうためこの値となる /*
#define N9 9     /* 分割後の切手片の切手枚数 /*
#define N0 0     /* 基準値 /*
#define N1 1     /* 最小の単位 /*
#define N2 2     /* N1 + N1 /*
#define N10 10   /* N9 + N1 /*
#define N16 16   /* 分割後の切手片の切手枚数 /*
#define N17 17   /* N16 * N1 /*
#define M0 0     /* 鏡像チェックで使用する。xに対する索引値 /*
#define M1 1     /* 鏡像チェックで使用する。(Z4 - x)に対する索引値 /*
#define M2 2     /* 鏡像チェックで使用する。yに対する索引値 /*
#define M3 3     /* 鏡像チェックで使用する。(Z4 - y)に対する索引値 /*
#define NP1 1    /* 分割後の片を構成する片の最小数 /*
#define NP2 2    /* 分割後の片を構成する片の数(両者が同じ場合) /*
#define NP3 3    /* 分割後の片を構成する片の最大数 /*
#define NP4 4    /* 分割前の片の数 /*
#define ID3 0    /* 3×3を識別するID /*
#define ID4 1    /* 4×4を識別するID /*
#define NON - N1 /* そこに切手がない状態 /*
typedef struct {
    int x; /* X座標 /*
    int y; /* Y座標 /*
} XY; /* XY座標 /*
typedef struct {
    int ban[Z4][Z4]; /* 切手 /*
    XY xy[N16]; /* 切手がある座標 /*
} PIECE; /* 切手片定義の構造体 /*
typedef struct {
    int ban[Z5][Z5]; /*  /*
} BAN5; /* 既出正解パターン /*
typedef struct {
    int n; /* 片を構成する切手枚数 /*
    int j; /* その中の順番 /*
} CUTPIECE;
PIECE *piece[N2][N17]; /* 切手片(全データ) /*
int nPiece[2][N17]; /* 枚数ごとの切手片の種類数 /*
BAN5 *ban5; /* 正解パターン /*
int nBan5 = N0; /* 正解数 /*
int nH4[N2]; /* 3×3、4×4の各使用片数 /*
int fCheck; /* 分割後に正方形になるかどうかのフラグ /*
static int len[] = {Z3 , Z4};   /* 分割後の辺の長さ /*
static int len2[] = {Z5 , Z7};  /* len[] * N2 - N1 /*
static int len3[] = {N9 , N16}; /* len[] * len[] /*
/******************************************************************************
機能:3×3、4×4全ての片を求める再帰処理
   既出パターンかチェックし、新規パターンなら登録する
引数:
 (1) ban 切手を置く盤
 (2) n   置いた切手の枚数
 (3) id  3×3、4×4を識別するID(ID3:3×3、ID4:4×4)
戻り値:なし
*****************************************************************************/*
void initSub(int ban[Z7][Z7] , int n , int id)
{
    int x , y; /* 座標 /*
    int i; /* ループの制御変数 /*
    int xmin = Z7 , ymin = Z7 , xmax = - N1 , ymax = - N1;
        /* 置いた切手の範囲 /*
    int xz , yz = - N1; /* 置いた切手片の基準座標(Y最小の中でX最小) /*
    int t1 = N0;
    int w1[Z7][Z7]; /* ワーク用盤 /*
    for(y = N0 ; y < len2[id] ; y++) { /* 範囲を求める /*
        for(x = N0 ; x < len2[id] ; x++) {
            if(ban[y][x] == ON) {
                if(x < xmin) xmin = x;
                if(y < ymin) ymin = y;
                if(x > xmax) xmax = x;
                if(y > ymax) ymax = y;
            }
        }
    }
    if(xmax - xmin >= len[id]) return; /* 範囲を超えたら対象外 /*
    if(ymax - ymin >= len[id]) return;
    for(y = N0 ; y < len[id] ; y++) {
        for(x = N0 ; x < len[id] ; x++) {
            w1[y][x] = OFF;
        }
    }
    for(y = N0 ; y < len2[id]; y++) {
        for(x = N0 ; x < len2[id] ; x++) {
            if(ban[y][x] == ON) {
                w1[y - ymin][x - xmin] = ON; /* 原点方向へシフトする /*
                if(yz == - N1) {
                    xz = x - xmin;
                    yz = y - ymin;
                }
            }
        }
    }
    for(i = N0 ; i < nPiece[id][n] ; i++) { /* 既出パターンかチェックする /*
        for(y = N0 ; y < len[id] ; y++) {
            for(x = N0 ; x < len[id] ; x++) {
                if(w1[y][x] != piece[id][n][i].ban[y][x]) break;
            }
            if(x < len[id]) break;
        }
        if(y == len[id]) return;
    }
    piece[id][n] = /*  新規パターン登録用のメモリ確保 /*
        (PIECE *)realloc(piece[id][n] , sizeof(PIECE) * (nPiece[id][n] + N1));
    if(piece[id][n] == NULL) {
        fprintf(stderr , "realloc error at %d\n" , __LINE__);
        exit(EXIT_FAILURE);
    }
    for(y = N0 ; y < len[id] ; y++) { /* 盤の登録 /*
        for(x = N0 ; x < len[id] ; x++) {
            piece[id][n][nPiece[id][n]].ban[y][x] = w1[y][x];
            if(w1[y][x] == ON) {
                piece[id][n][nPiece[id][n]].xy[t1].x = x - xz;
                piece[id][n][nPiece[id][n]].xy[t1++].y = y - yz;
            }
        }
    }
    nPiece[id][n]++;
    if(n == len3[id]) return;
    for(y = N0 ; y < len2[id] ; y++) { /* 座標の登録 /*
        for(x = N0 ; x < len2[id] ; x++) {
           if(ban[y][x] == ON) continue;
           if((x > N0 && ban[y][x - N1] == ON) ||
              (x < len2[id] - N1 && ban[y][x + N1] == ON) ||
              (y > N0 && ban[y - N1][x] == ON) ||
              (y <  len2[id] - N1 && ban[y + N1][x] == ON)) {
               ban[y][x] = ON;
               initSub(ban , n + N1 , id);
               ban[y][x] = OFF;
           }
       }
   }
}
/******************************************************************************
機能:初期化
   全ての3×3、4×4用の片を求める
引数:なし
戻り値:なし
*****************************************************************************/*
void init(void)
{
    int i; /* ループの制御変数 /*
    int ban[Z7][Z7]; /* 盤 /*
    int x , y; /* 座標 /*
    for(y = N0 ; y < Z7 ; y++) { /* 盤の初期化 /*
        for(x = N0 ; x < Z7 ; x++) ban[y][x] = OFF;
    }
    for(i = N1 ; i <= N9 ; i++) nPiece[ID3][i] = N0; /* 3×3取得処理 /*
    ban[Z3 - N1][Z3 - N1] = ON;
    initSub(ban , N1 , ID3);
    ban[Z3 - N1][Z3 - N1] = OFF;
    for(i = N1 ; i <= N16 ; i++) nPiece[ID4][i] = N0; /* 4×4取得処理 /*
    ban[Z4 - N1][Z4 - N1] = ON;
    initSub(ban , N1 , ID4);
}
/******************************************************************************
機能:そこに切手片を置けるかどうかチェックし、置けるなら置く。
引数:
  (1) ban  : 切手片を置く盤
  (2) x    : 切手片を置く基準X座標
  (3) y    : 切手片を置く基準Y座標
  (4) i    : 切手片の切手枚数
  (5) j    : 上記の中の一連番号
  (6) id   : 3×3、4×4を識別するID
  (7) val  : 設定する値
  (8) size : 置く範囲
戻り値:OFF:置けない、ON:置ける
*****************************************************************************/*
int okeruka(int ban[Z5][Z5] , int x , int y , int i , int j , int id ,
    int val , int size)
{
    int k;
    for(k = N0 ; k < i ; k++) {
        if(x + piece[id][i][j].xy[k].x < N0) return(OFF); /* 盤の外 /*
        if(x + piece[id][i][j].xy[k].x >= size) return(OFF);
        if(y + piece[id][i][j].xy[k].y < N0) return(OFF);
        if(y + piece[id][i][j].xy[k].y >= size) return(OFF);
        if(ban[y + piece[id][i][j].xy[k].y][x + piece[id][i][j].xy[k].x] != NON)
            return(OFF); /* 既に切手がある /*
    }
    for(k = N0 ; k < i ; k++) { /* 置く /*
        ban[y + piece[id][i][j].xy[k].y][x + piece[id][i][j].xy[k].x] = val;
    }
    return(ON);
}
/******************************************************************************
機能:切手片を取り除く
引数:
  (1) ban  : 切手片を置く盤
  (2) x    : 切手片を置く基準X座標
  (3) y    : 切手片を置く基準Y座標
  (4) i    : 切手片の切手枚数
  (5) j    : 上記の中の一連番号
  (6) id   : 3×3、4×4を識別するID
戻り値:なし
*****************************************************************************/*
void toru(int ban[Z5][Z5] , int x , int y , int i , int j , int id)
{
    int k; /* ループの制御変数 /*
    for(k = N0 ; k < i ; k++) {
        ban[y + piece[id][i][j].xy[k].y][x + piece[id][i][j].xy[k].x] = NON;
    }
}
/******************************************************************************
機能:3×3、4×4の正方形を作れるかどうかチェックする再帰関数
引数:
  (1) ban      : 盤
  (2) cutPiece : 使用する片
  (3) fUsed    : 使用済みフラグ
  (4) x        : X座標
  (5) y        : Y座標
  (6) nH       : 片の数
  (7) id       : 3×3、4×4を識別するID
  (8) n        : 片の何番目か
戻り値:なし
*****************************************************************************/*
void checkSub(int ban[Z5][Z5] , CUTPIECE cutPiece[N2][NP3] , int fUsed[NP3] ,
    int x , int y , int nH , int id , int n)
{
    int x2 , y2; /* 座標 /*
    int i; /* ループの制御変数 /*
    if(y == len[id]) {
        fCheck = ON;
        return;
    }
    x2 = x + N1; /* 次の座標を求める /*
    y2 = y;
    if(x2 == len[id]) {
        x2 = N0;
        y2++;
    }
    if(ban[y][x] != NON) { /* 既に切手が置いてあった場合は次の座標へ /*
        checkSub(ban , cutPiece , fUsed , x2 , y2 , nH , id , n);
        return;
    }
    for(i = N0 ; i < nH ; i++) {
        if(fUsed[i] == ON) continue; /* 使用済み /*
        if(okeruka(ban , x , y , cutPiece[id][i].n , cutPiece[id][i].j , id ,
            n , Z4) == OFF) continue;
        fUsed[i] = ON;
        checkSub(ban , cutPiece , fUsed , x2 , y2 , nH , id , n + N1);
        fUsed[i] = OFF;
        toru(ban , x , y , cutPiece[id][i].n , cutPiece[id][i].j , id);
    }
}
/******************************************************************************
機能:選んだ片でそれぞれ3×3、4×4の正方形を作れるか
   5×5が既出でないかどうかチェックする
引数:
  (1) cutPiece : 使用する片
  (2) ban      : 盤
戻り値:OFF:駄目、ON:チェック結果OK
*****************************************************************************/*
int check(CUTPIECE cutPiece[N2][NP3] , int ban[Z5][Z5])
{
    int i , j , k; /* ループの制御変数 /*
    int x , y; /* 座標 /*
    int fUsed[NP3]; /* 使用済みフラグ /*
    int w2[Z5][Z5];
    int t1[NP4];
    int x2 , y2; /* 座標 /*
    int y1[NP4][N2];
    int nY1;
    static XY xy[] = {M1,M2 , M0,M3 , M1,M3 , M2,M0 , M3,M0 , M2,M1 , M3,M1};
        /* 座標変換パターン /*
    for(i = N0 ; i < nH4[ID3] ; i++) fUsed[i] = OFF;
    for(y = N0 ; y < Z3 ; y++) {
        for(x = N0 ; x < Z3 ; x++) w2[y][x] = NON;
    }
    fCheck = OFF;
    checkSub(w2 , cutPiece , fUsed , N0 , N0 , nH4[ID3] , ID3 , N0);
    if(fCheck == OFF) return(OFF);
    for(i = N0 ; i < nH4[ID4] ; i++) fUsed[i] = OFF;
    for(y = N0 ; y < Z4 ; y++) {
        for(x = N0 ; x < Z4 ; x++) w2[y][x] = NON;
    }
    fCheck = OFF;
    checkSub(w2 , cutPiece , fUsed , N0 , N0 , nH4[ID4] , ID4 , N0);
    if(fCheck == OFF) return(OFF);
    for(i = N0 ; i < nBan5 ; i++) {
        for(j = N0 ; j < sizeof(xy) / sizeof(XY) ; j++) {
            for(y = N0 ; y < Z5 ; y++) {
                t1[M2] = y; /* Y座標そのまま /*
                t1[M3] = Z5 - y - N1; /* Y座標反転 /*
                for(x = N0 ; x < Z5 ; x++) {
                    t1[M0] = x; /* X座標そのまま /*
                    t1[M1] = Z5 - x - N1; /* X座標反転 /*
                    w2[y][x] = ban5[i].ban[t1[xy[j].y]][t1[xy[j].x]];
                }
            }
            nY1 = N0;
            for(y2 = N0 ; y2 < Z5 ; y2++) {
                for(x2 = N0 ; x2 < Z5 ; x2++) {
                    for(k = N0 ; k < nY1 ; k++) {
                        if(y1[k][N0] == ban[y2][x2] && y1[k][N1] == w2[y2][x2])
                            break;
                    }
                    if(k == nY1) {
                        if(nY1 == NP4) break;
                        y1[nY1][N0] = ban[y2][x2];
                        y1[nY1][N1] = w2[y2][x2];
                        nY1++;
                    }
                }
                if(x2 < Z5) break;
            }
            if(y2 == Z5) return(OFF);
        }
    }
    ban5 = (BAN5 *)realloc(ban5 , sizeof(BAN5) * (nBan5 + N1));
    if(ban5 == NULL) { /* メモリ確保エラー /*
        free(piece);
        fprintf(stderr , "realloc error at %d\n" , __LINE__);
        exit(EXIT_FAILURE);
    }
    for(y = N0 ; y < Z5 ; y++) {
        for(x = N0 ; x < Z5 ; x++) ban5[nBan5].ban[y][x] = ban[y][x];
    }
    nBan5++;
    return(ON);
}
/******************************************************************************
機能:5×5を埋めていく再帰関数
引数:
  (1) ban      : 盤
  (2) x        : X座標
  (3) y        : Y座標
  (4) usedNo   : 切手枚数ごとの片の使用数
  (5) cutPiece : 使用する片
  (6) mH3      : 3×3の片の内の何番目か
  (7) mH4      : 4×4の片の内の何番目か
  (8) n        : 何番目の片か
戻り値:なし
*****************************************************************************/*
void sub(int ban[Z5][Z5] , int x , int y , int usedNo[N2][N17] ,
    CUTPIECE cutPiece[N2][NP3] , int mH3 , int mH4 , int n)
{
    int x2 , y2; /* 座標 /*
    int i , j; /* ループの制御変数 /*
    static char *stamp[] = {"□" , "■" , "○" , "●"}; /* 正解例表示用 /*
    if(y == Z5) { /* 5×5を全て埋めた場合 /*
        if(check(cutPiece , ban) == ON) {
            for(y2 = N0 ; y2 < Z5 ; y2++) { /* 正解例表示 /*
                for(x2 = N0 ; x2 < Z5 ; x2++) 
                    printf("%s" , stamp[ban[y2][x2]]);
                printf("\n");
            }
            printf("\n");
        }
        return;
    }
    x2 = x + N1; /* 次の座標 /*
    y2 = y;
    if(x2 == Z5) {
        x2 = N0;
        y2++;
    }
    if(ban[y][x] != NON) { /* 既に切手が置いてあった場合は次の座標へ /*
        sub(ban , x2 , y2 , usedNo , cutPiece , mH3 , mH4 , n);
        return;
    }
    for(i = N1 ; i <= N9 ; i++) { /* 3×3用の片を置く /*
        if(usedNo[ID3][i] == N0) continue;
        for(j = N0 ; j < nPiece[ID3][i] ; j++) {
            if(okeruka(ban , x , y , i , j , ID3 , n , Z5) == OFF) continue;
            usedNo[ID3][i]--;
            cutPiece[ID3][mH3].n = i;
            cutPiece[ID3][mH3].j = j;
            sub(ban , x2 , y2 , usedNo , cutPiece , mH3 + N1 , mH4 , n + N1);
            usedNo[ID3][i]++;
            toru(ban , x , y , i , j , ID3);
        }
    }
    for(i = N1 ; i <= N16 ; i++) {  /* 4×4用の片を置く /*
        if(usedNo[ID4][i] == N0) continue;
        for(j = N0 ; j < nPiece[ID4][i] ; j++) {
            if(okeruka(ban , x , y , i , j , ID4 , n , Z5) == OFF) continue;
            usedNo[ID4][i]--;
            cutPiece[ID4][mH4].n = i;
            cutPiece[ID4][mH4].j = j;
            sub(ban , x2 , y2 , usedNo , cutPiece , mH3 , mH4 + N1 , n + N1);
            usedNo[ID4][i]++;
            toru(ban , x , y , i , j , ID4);
        }
    }
}
/******************************************************************************
機能:引数で渡された切手枚数の片ごとに5×5を埋められるか探査する
引数:
  (1) p0 : 3×3片の1個目の切手枚数
  (2) p1 : 3×3片の2個目の切手枚数
  (3) p2 : 4×4片の1個目の切手枚数
  (4) p3 : 4×4片の2個目の切手枚数
戻り値:なし
*****************************************************************************/*
void set4(int p0 , int p1 , int p3 , int p4)
{
    int usedNo[N2][N17]; /* 切手枚数ごとの片の使用数 /*
    int i; /* ループの制御変数 /*
    int x , y; /* 座標 /*
    int p2 , p5; /* 3個目の数 /*
    int ban[Z5][Z5]; /* 切手を置く盤 /*
    CUTPIECE cutPiece[N2][NP3]; /* 使用する片 /*
    for(i = N1 ; i <= N9  ; i++) usedNo[ID3][i] = N0; /* 0に初期化 /*
    for(i = N1 ; i <= N16 ; i++) usedNo[ID4][i] = N0;
    usedNo[ID3][p0]++;             /* 3×3 1個目 /*
    if(p1 > N0) usedNo[ID3][p1]++; /* 3×3 2個目 /*
    p2 = N9 - p0 - p1;
    if(p2 > N0) usedNo[ID3][p2]++; /* 3×3 3個目 /*
    usedNo[ID4][p3]++;             /* 4×4 1個目 /*
    if(p4 > N0) usedNo[ID4][p4]++; /* 4×4 2個目 /*
    p5 = N16 - p3 - p4;
    if(p5 > N0) usedNo[ID4][p5]++; /* 4×4 3個目 /*
    for(y = N0 ; y < Z5 ; y++) {
        for(x = N0 ; x < Z5 ; x++) ban[y][x] = NON; /* 切手がない状態にする /*
    }
    sub(ban , N0 , N0 , usedNo , cutPiece , N0 , N0 , N0);
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:EXIT_SUCCESS
*****************************************************************************/*
int main(void)
{
    int i , j , k; /* ループの制御変数 /*
    for(i = N1 ; i <= N9 ; i++) { /* 3×3パターンを格納する領域の確保 /*
        piece[ID3][i] = (PIECE *)malloc(sizeof(PIECE));
        if(piece[ID3][i] == NULL) { /* メモリ確保エラー /*
            fprintf(stderr , "malloc error at %d\n" , __LINE__);
            exit(EXIT_FAILURE);
        }
    }
    for(i = N1 ; i <= N16 ; i++) { /* 4×4パターンを格納する領域の確保 /*
        piece[ID4][i] = (PIECE *)malloc(sizeof(PIECE));
        if(piece[ID4][i] == NULL) { /* メモリ確保エラー /*
            for(i = N1 ; i <= N9 ; i++) free(piece[ID4][i]);
            fprintf(stderr , "malloc error at %d\n" , __LINE__);
            exit(EXIT_FAILURE);
        }
    }
    ban5 = (BAN5 *)malloc(sizeof(BAN5)); /* 5×5(解答)領域の確保 /*
    if(ban5 == NULL) { /* メモリ確保エラー /*
        for(i = N1 ; i <= N9  ; i++) free(piece[ID3][i]);
        for(i = N1 ; i <= N16 ; i++) free(piece[ID4][i]);
        fprintf(stderr , "malloc error at %d\n" , __LINE__);
        exit(EXIT_FAILURE);
    }
    init();
    nH4[ID3] = NP1;                   /* 3×3を1個、4×4を3個の場合 /*
    nH4[ID4] = NP3;
    for(i = N1 ; i <= N16 ; i++) {    /* 4×4片の1個目の切手枚数 /*
        for(j = i ; j < N16 ; j++) {  /* 4×4片の2個目の切手枚数 /*
            k = N16 - i - j;          /* 4×4片の3個目の切手枚数 /*
            if(k < j) break;
            set4(N9 , N0 , i , j);
        }
    }
    nH4[ID3] = nH4[ID4] = NP2;        /* 3×3を2個、4×4を2個の場合 /*
    for(i = N1 ; i <= N9 ; i++) {     /* 3×3片の1個目の切手枚数 /*
        if(i > N9 - i) break;         /* 3×3片の2個目の切手枚数 /*
        for(j = i ; j <= N16 ; j++) { /* 4×4片の1個目の切手枚数 /*
            if(j > N16 - j) break;    /* 4×4片の2個目の切手枚数 /*
            set4(i , N9 - i , j , N16 - j);
        }
    }
    nH4[ID3] = NP3;                   /* 3×3を3個、4×4を1個の場合 /*
    nH4[ID4] = NP1;
    for(i = N1 ; i <= N9 ; i++) {     /* 3×3片の1個目の切手枚数 /*
        for(j = i ; j <= N9 ; j++) {  /* 3×3片の2個目の切手枚数 /*
            k = N9 - i - j;           /* 3×3片の3個目の切手枚数 /*
            if(k < j) break;
            set4(i , j , N16 , N0);
        }
    }
    free(ban5); /* メモリ開放 /*
    for(i = N1 ; i <= N9  ; i++) free(piece[ID3][i]); /* メモリ開放 /*
    for(i = N1 ; i <= N16 ; i++) free(piece[ID4][i]);
    printf("%d通り\n" , nBan5);
    return(EXIT_SUCCESS); /* 正常終了 /*
}


第160回(7月)カーペンターズ・パズル

/******************************************************************************
Cマガジン2004年4月号電脳クラブ出題
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#define OFF 0 /* オフ /*
#define ON  1 /* オン /*
#define N0 0 /* 基準の値 /*
#define N1 1 /* 単位の数 /*
#define N2 2 /* N1 + N1 /*
#define ERRMSG "malloc error at %d\n" /* メモリ確保エラーメッセージ /*
#define AKI "□" /* コインなし /*
#define COIN "●" /* コインあり /*
typedef struct {
    unsigned int ban; /* 盤番号 /*
    int min; /* その盤番号までの手数の最小値 /*
} BANMIN; /* 盤番号と最小手数の対応 /*
unsigned int *orBit; /* 盤番号ビット(OR) /*
unsigned int *andBit; /* 盤番号ビット(AND) /*
unsigned int startBan = N0 , goalBan = N0; /* 初期、終了時の盤番号 /*
BANMIN *banmin; /* 盤番号と最小手数の対応 /*
int fEnd = OFF; /* 探査終了フラグ /*
int nx; /* 盤の横方向の大きさ /*
int ny; /* 盤の縦方向の大きさ /*
int nxy; /* nx * ny /*
int nCoin = N0; /* コインの数 /*
int nBan , nBan1; /* 盤パターンの数、及びそのマイナス1 /*
unsigned int *hist; /* 最短手順 /*
int min = 1000000000; /* 最小手数 /*
static char *startJotai[] = { /* 盤の初期状態 /*
    "***   " ,
    "***   " ,
    "***   " ,
    "      " };
static char *goalJotai[] = { /* 盤の終了状態 /*
    "   ***" ,
    "   ***" ,
    "   ***" ,
    "      " };
/******************************************************************************
機能:その盤の最小手数を求める。(2分岐探査)
引数:
  (1) ban : 盤番号
戻り値:盤番号に対応する最小手数
*****************************************************************************/*
int getMin(unsigned int ban)
{
    int is = N0 , ie , im; /* 下限、上限、中間 /*
    ie = nBan;
    while(N1) {
        im = (is + ie) / N2;
        if(banmin[im].ban == ban) return(banmin[im].min);
        else if(banmin[im].ban < ban) is = im;
        else ie = im;
    }
}
/******************************************************************************
機能:その盤に最小値をセットする。(2分岐探査)
引数:
  (1) ban : 盤番号
  (2) min : 最小手数
戻り値:なし
*****************************************************************************/*
void setMin(unsigned int ban , int min)
{
    int is = N0 , ie , im; /* 下限、上限、中間 /*
    ie = nBan;
    while(N1) {
        im = (is + ie) / N2;
        if(banmin[im].ban == ban) {
            banmin[im].min = min;
            return;
        }
        else if(banmin[im].ban < ban) is = im;
        else                          ie = im;
    }
}
/******************************************************************************
機能:盤の中のある場所の情報をセットする。
引数:
  ban : 盤番号
  pos : セットする座標番号
  f1  : セットする値(OFF:コインなし、ON:コインあり)
戻り値:なし
*****************************************************************************/*
void setBan(unsigned int *ban , int pos , int f1)
{
    if(f1 == OFF) *ban &= andBit[pos];
    else          *ban |= orBit[pos];
}
/******************************************************************************
機能:盤の中のある場所の情報を取得する。
引数:
  ban : 盤番号
  pos : 座標番号
戻り値:
  OFF : コインがない
  ON  : コインがある
*****************************************************************************/*
int getBan(unsigned int ban , int pos)
{
    int t1;
    t1 = ban & orBit[pos];
    if(t1 == N0) return(OFF);
    else         return(ON);
}
/******************************************************************************
機能:結果表示
引数:
  n : 手数
戻り値:なし
*****************************************************************************/*
void disp(int n)
{
    int i , j; /* ループの制御変数 /*
    int t1; /* 一時変数 /*
    for(i = N0 ; i < n ; i++) {
        if(i == N0) printf("<初期状態>\n");
        else        printf("<%d手目>\n" , i);
        for(j = N0 ; j < nxy ; j++) {
            t1 = getBan(hist[i] , j); /* その座標にコインがあるか /*
            if(t1 == OFF) printf(AKI); /* コインなし /*
            else          printf(COIN); /* コインあり /*
            if(((j - nx + N1) % nx) == N0) printf("\n");
        }
    }
    printf("<%d手目>\n" , n);
    for(j = N0 ; j < nxy ; j++) {
        t1 = getBan(goalBan , j); /* その座標にコインがあるか /*
        if(t1 == OFF) printf(AKI); /* コインなし /*
        else          printf(COIN); /* コインあり /*
        if(((j - nx + N1) % nx) == N0) printf("\n");
    }
}
/******************************************************************************
機能:qsortの比較関数
引数:
  (1) a : 左側ポインタ
  (2) b : 右側ポインタ
戻り値:qsortの規定に従う。
*****************************************************************************/*
int cmp(const void **a , const void **b)
{
    if     (((BANMIN *)a)->ban <  ((BANMIN *)b)->ban) return(-N1);
    else if(((BANMIN *)a)->ban == ((BANMIN *)b)->ban) return(N0);
    else                                              return(N1);
}
int nn = N0;
/******************************************************************************
機能:全ての盤の番号を求める。
引数:
  (1) r1 : コインの座標番号
  (2) m  : コインを置ける最初の座標
  (3) n  : 残りのコインの数
戻り値:なし
*****************************************************************************/*
void init2(int *coinZ , int m , int n)
{
    int i;
    if(n == N0) { /* コインを全部置いた /*
        banmin[nn].ban = N0;
        for(i = N0 ; i < nCoin ; i++) {
            setBan(&(banmin[nn].ban) , coinZ[i] , ON);
        }
        nn++;
        return;
    }
    for(i = m ; i < nxy ; i++) { /* コインを1個置いて次へ /*
        coinZ[n - N1] = i;
        init2(coinZ , i + N1 , n - N1);
    }
}
/******************************************************************************
機能:理論上あり得る最小手数を求める。
引数:
  (1) startCoin 移動前 
  (2) goalCoin  移動後
  (3) n         残りのコイン
戻り値:なし
*****************************************************************************/*
void calcMin(int startCoin[] , int goalCoin[] , int n)
{
    int i , j; /* ループの制御変数 /*
    int tesu; /* 移動に必要な手数 /*
    int sx , sy; /* コインの移動座標量 /*
    int t1; /* 一時変数 /*
    for(i = N0 ; i < n ; i++) {
        t1 = goalCoin[i];
        goalCoin[i] = goalCoin[n - N1];
        goalCoin[n - N1] = t1;
        if(n == N2) {
            tesu = N0;
            for(j = N0 ; j < nCoin ; j++) {
                sx = abs(startCoin[j] % nx - goalCoin[j] % nx);
                sy = abs(startCoin[j] / nx - goalCoin[j] / nx);
                while(sx >= N2 && sy >= N2) {
                    sx -= N2; /* X、Yともにプラスマイナス2離れている /*
                    sy -= N2;
                    tesu++;
                }
                while(sx >= N2) {
                    sx -= N2; /* Xがプラスマイナス2離れている /*
                    tesu++;
                }
                while(sy >= N2) {
                    sy -= N2; /* Yがプラスマイナス2離れている /*
                    tesu++;
                }
                if(sx > N0) tesu++; /* Xがプラスマイナス1離れている /*
                if(sy > N0) tesu++; /* Yがプラスマイナス1離れている /*
            }
            if(tesu < min) min = tesu;
        }
        else calcMin(startCoin , goalCoin , n - 1);
        t1 = goalCoin[i];
        goalCoin[i] = goalCoin[n - N1];
        goalCoin[n - N1] = t1;
    }
}
/******************************************************************************
機能:初期化
引数:なし
戻り値:なし
*****************************************************************************/*
void init(void)
{
    int i , j; /* ループの制御変数 /*
    int nw = N1; /* 2のコイン数乗−1 /*
    int n = N0;
    int x , y; /* 座標 /*
    int t1 = N1 , t2 = N1; /* 一時変数 /*
    int x1 = N0 , y1 = N0 , x2 = N0 , y2 = N0; /* 座標 /*
    int *coinZ; /* コインの座標番号 /*
    int *startCoin; /* 初期状態コインの座標番号 /*
    int *goalCoin;  /* 終了状態コインの座標番号 /*
    static int sosu[] = {2 , 3 , 5 , 7}; /* 素数列 /*
    ny = sizeof(startJotai) / sizeof(startJotai[N0]);
    nx = strlen(startJotai[N0]);
    nxy = nx * ny;
    orBit = (int *)malloc(sizeof(int) * nxy);
    if(orBit == NULL) {
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
    andBit = (int *)malloc(sizeof(int) * nxy);
    if(andBit == NULL) {
        free(orBit);
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
    for(i = N0 ; i < nxy ; i++) nw *= N2;
    nw--;
    orBit[N0] = N1;
    for(i = N1 ; i < nxy ; i++) orBit[i] = orBit[i - N1] * N2;
    for(i = N0 ; i < nxy ; i++) andBit[i] = nw - orBit[i];
    for(y = N0 ; y < ny ; y++) {
        for(x = N0 ; x < nx ; x++) {
            if(*(startJotai[y] + x) == '*') {
                setBan(&startBan , n , ON); /* 初期状態でコインあり /*
                nCoin++; /* コインを数える /*
            }
            if(*(goalJotai[y] + x) == '*') {
                setBan(&goalBan , n , ON); /* 終了状態でコインあり /*
            }
            n++;
        }
    }
    startCoin = (int *)malloc(sizeof(int) * nCoin);
    if(startCoin == NULL) {
        free(orBit);
        free(andBit);
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
    n = N0;
    for(y = N0 ; y < ny ; y++) { /* 初期状態のコインの座標番号 /*
        for(x = N0 ; x < nx ; x++) {
            if(*(startJotai[y] + x) == '*') startCoin[n++] = y * nx + x;
        }
    }
    goalCoin = (int *)malloc(sizeof(int) * nCoin);
    if(goalCoin == NULL) {
        free(orBit);
        free(andBit);
        free(startCoin);
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
    n = N0;
    for(y = N0 ; y < ny ; y++) { /* 終了状態のコインの座標番号 /*
        for(x = N0 ; x < nx ; x++) {
            if(*(goalJotai[y] + x) == '*') goalCoin[n++] = y * nx + x;
        }
    }
    n = N0;
    for(i = N1 ; i <= nCoin ; i++) {
        t1 *= (nxy - nCoin + i);
        t2 *= i;
        for(j = N0 ; j < sizeof(sosu) / sizeof(sosu[N0]) ; j++) {
            while((t1 % sosu[j]) == N0 && (t2 % sosu[j]) == N0) {
                t1 /= sosu[j]; /* オーバフローしないよう、あらかじめ約分 /*
                t2 /= sosu[j];
            }
        }
    }
    nBan = t1 / t2;
    nBan1 = nBan - N1;
    banmin = (BANMIN *)malloc(sizeof(BANMIN) * (nBan + N1));
    if(banmin == NULL) {
        free(orBit);
        free(andBit);
        free(startCoin);
        free(goalCoin);
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
    coinZ = (int *)malloc(sizeof(int) * nCoin);
    if(coinZ == NULL) {
        free(orBit);
        free(andBit);
        free(startCoin);
        free(goalCoin);
        free(banmin);
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
    init2(coinZ , N0 , nCoin);
    free(coinZ);
    qsort((void *)banmin , nBan , sizeof(BANMIN) , cmp);
    banmin[nBan].min = banmin[nBan1].min;
    banmin[nBan].ban = banmin[nBan1].ban;
    calcMin(startCoin , goalCoin , nCoin);
    free(startCoin);
    free(goalCoin);
    hist = (int *)malloc(sizeof(int) * nx * nCoin);
    if(hist == NULL) {
        free(orBit);
        free(andBit);
        free(banmin);
        fprintf(stderr , ERRMSG , __LINE__);
        exit(EXIT_FAILURE);
    }
}
/******************************************************************************
機能:探査用再帰関数
引数:
  (1) ban : 盤番号
  (2) n   : 手数
戻り値:なし
*****************************************************************************/*
void sub(unsigned int ban , int n)
{
    int i; /* ループの制御変数(座標番号) /*
    int x , y; /* 現在のコインの座標 /*
    int n1; /* 次の手数 /*
    int i1 , i2; /* コイン移動後の座標番号 /*
    int dx , dy; /* 座標の増分 /*
    int x1 , y1 , x2 , y2; /* コイン移動後の座標 /*
    unsigned int t1; /* 一時変数 /*
    if(fEnd == ON) return;
    if(getMin(ban) <= n) return;
    setMin(ban , n);
    hist[n] = ban;
    if(ban == goalBan) { /* ゴールにたどり着いたら /*
        fEnd = ON; /* 終了フラグをONにする /*
        disp(n); /* 途中の手順を表示する /*
        return;
    }
    n1 = n + N1; /* 次の手数 /*
    t1 = ban; /* 現在の盤番号を記録 /*
    for(i = N0 ; i < nxy ; i++) {
        if(getBan(ban , i) == OFF) continue;
        x = i % nx; /* 現在のX座標 /*
        y = i / nx; /* 現在のY座標 /*
        for(dy = - N1 ; dy < N2 ; dy++) { /* Y座標の増分 /*
            y1 = y + dy; /* 1マス移動後のY座標 /*
            if(y1 == - N1 || y1 == ny) continue;
            y2 = y1 + dy; /* 2マス移動後のY座標 /*
            for(dx = - N1 ; dx < N2 ; dx++) { /* X座標の増分 /*
                if(dx == N0 && dy == N0) continue;
                x1 = x + dx; /* 1マス移動後のX座標 /*
                if(x1 == - N1 || x1 == nx) continue;
                i1 = i + dx + nx * dy;
                if(getBan(ban , i1) == OFF) { /* 1マス動く /*
                    setBan(&ban , i  , OFF);
                    setBan(&ban , i1 , ON);
                    sub(ban , n1);
                    ban = t1; /* 元に戻す /*
                }
                if(y2 == - N1 || y2 == ny) continue;
                x2 = x1 + dx; /* 2マス移動後のX座標 /*
                if(x2 == - N1 || x2 == nx) continue;
                i2 = i1 + dx + nx * dy;
                if(getBan(ban , i1) == ON && getBan(ban , i2) == OFF) {
                    setBan(&ban , i  , OFF); /* 2マス動く /*
                    setBan(&ban , i2 , ON);
                    sub(ban , n1);
                    ban = t1; /* 元に戻す /*
                }
            }
        }
    }
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:EXIT_SUCCESS
*****************************************************************************/*
int main(void)
{
    int i; /* ループの制御変数 /*
    init(); /* 初期化処理 /*
    while(fEnd == OFF) { /* 見つかるまでループする /*
        for(i = N0 ; i <= nBan ; i++) banmin[i].min = min + N1;
        sub(startBan , N0);
        min++;
    }
    free(orBit);
    free(andBit);
    free(banmin);
    free(hist);
    return(EXIT_SUCCESS);
}


第158回(5月)肩のせ文字の誤植

/*****************************************************************************
肩のせ文字の誤植(Cマガジン2004年2月号出題)
****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#define N0 0 /* 原点 /*
#define N1 1 /* 1 /*
#define N2 2 /* 2 /*
#define N3 3 /* 3 /*
#define N4 4 /* 4 /*
#define N5 5 /* 5 /*
#define N6 6 /* 6 /*
#define N10           10 /* 10進数の10 /*
#define N100         100 /* 10 * 10 /*
#define N1000       1000 /* 10 * 10 * 10 /*
#define N10000     10000 /* 10 * 10 * 10 * 10 /*
#define N100000   100000 /* 10 * 10 * 10 * 10 * 10 * 10 /*
#define N1000000 1000000 /* 10 * 10 * 10 * 10 * 10 * 10 * 10 /*
#define NPAT 10 /* 6桁をa、b、cに分ける桁数パターンの数 /*
typedef struct {
    int a_k; /* aの桁数−1 /*
    int b_s; /* bの開始桁 /*
    int b_k; /* bの桁数−1 /*
    int c_s; /* cの開始桁 /*
    int c_k; /* cの桁数−1 /*
} PAT; /* 6桁数字を3つに分割するパターン /*
PAT pat[NPAT];
int z0[N6][N4]; /* 6桁数字を1、2、3、4桁に分割した数 /*
/*****************************************************************************
機能:題意を満たすかどうかチェックし、満たすなら表示する。
引数:
  (1) n5 : 右辺の6桁の数
戻り値:なし
****************************************************************************/*
void sub(int n5)
{
    int i , j; /* ループの制御変数 /*
    int s1; /* (a^b)*c /*
    for(i = N0 ; i < NPAT ; i++) {
        s1 = z0[pat[i].c_s][pat[i].c_k]; /* c /*
        for(j = N0 ; j < z0[pat[i].b_s][pat[i].b_k] ; j++) {
            s1 *= z0[N0][pat[i].a_k]; /* cにaをb個を掛け合わせる /*
            if(s1 >= N1000000) break; /* 7桁以上になったらbreak /*
        }
        if(s1 == n5) printf("(%d,%d,%d)\n" , /* 正解なら表示する /*
            z0[N0][pat[i].a_k] , z0[pat[i].b_s][pat[i].b_k] ,
            z0[pat[i].c_s][pat[i].c_k]);
    }
}
/*****************************************************************************
機能:メイン
引数:なし
戻り値:EXIT_SUCCESS
****************************************************************************/*
int main(void)
{
    int i , j; /* ループの制御変数 /*
    int n = N0;
    int d0_0 , d0_1 , d0_2 , d0_3; /* 10万の位 *10、*100、*1000 /*
    int d1_0 , d1_1 , d1_2 , d1_3; /* 1万の位 *10、*100、*1000 /*
    int d2_0 , d2_1 , d2_2 , d2_3; /* 1000の位 *10、*100、*1000 /*
    int d3_0 , d3_1 , d3_2; /* 100の位 *10、*100 /*
    int d4_0 , d4_1; /* 100の位 *10 /*
    int d5_0; /* 1の位 /*
    int n0 , n1 , n2 , n3 , n4 , n5; /* 6桁の数の値(各桁まで) /*
    for(i = N0 ; i < N4 ; i++) { /* 初期化 /*
        for(j = N0 ; j < N4 - i ; j++) {
            pat[n].a_k = i;
            pat[n].b_s = pat[n].a_k + N1;
            pat[n].b_k = j;
            pat[n].c_s = pat[n].a_k + pat[n].b_k + N2;
            pat[n].c_k = N5 - pat[n].c_s;
            n++;
        }
    }
    for(d0_0 = N1 ; d0_0 < N10 ; d0_0++) { /* 10万の位 /*
        d0_1 = d0_0 * N10;
        d0_2 = d0_0 * N100;
        d0_3 = d0_0 * N1000;
        n0 = d0_0 * N100000;
        z0[N0][N0] = d0_0;
        for(d1_0 = N0 ; d1_0 < N10 ; d1_0++) { /* 1万の位 /*
            d1_1 = d1_0 * N10;
            d1_2 = d1_0 * N100;
            n1 = n0 + d1_0 * N10000;
            z0[N1][N0] = d1_0;
            z0[N0][N1] = d0_0 * N10 + z0[N1][N0];
            d1_3 = d1_0 * N1000;
            for(d2_0 = N0 ; d2_0 < N10 ; d2_0++) { /* 1000の位 /*
                d2_1 = d2_0 * N10;
                d2_2 = d2_0 * N100;
                d2_3 = d2_0 * N1000;
                n2 = n1 + d2_3;
                z0[N2][N0] = d2_0;
                z0[N1][N1] = d1_1 + z0[N2][N0];
                z0[N0][N2] = d0_2 + z0[N1][N1];
                for(d3_0 = N0 ; d3_0 < N10 ; d3_0++) { /* 100の位 /*
                    d3_1 = d3_0 * N10;
                    d3_2 = d3_0 * N100;
                    n3 = n2 + d3_2;
                    z0[N3][N0] = d3_0;
                    z0[N2][N1] = d2_1 + z0[N3][N0];
                    z0[N1][N2] = d1_2 + z0[N2][N1];
                    z0[N0][N3] = d0_3 + z0[N1][N2];
                    for(d4_0 = N0 ; d4_0 < 10 ; d4_0++) { /* 10の位 /*
                        d4_1 = d4_0 * N10;
                        n4 = n3 + d4_1;
                        z0[N4][N0] = d4_0;
                        z0[N3][N1] = d3_1 + z0[N4][N0];
                        z0[N2][N2] = d2_2 + z0[N3][N1];
                        z0[N1][N3] = d1_3 + z0[N2][N2];
                        for(d5_0 = N0 ; d5_0 < N10 ; d5_0++) { /* 1の位 /*
                            n5 = n4 + d5_0;
                            z0[N5][N0] = d5_0;
                            z0[N4][N1] = d4_1 + z0[N5][N0];
                            z0[N3][N2] = d3_2 + z0[N4][N1];
                            z0[N2][N3] = d2_3 + z0[N3][N2];
                            sub(n5);
                        }
                    }
                }
            }
        }
    }
    return(EXIT_SUCCESS);
}


第156回(3月)ダブりペントミノ

/******************************************************************************
ダブりペントミノ(Cマガジン2003年12月電脳クラブ出題)
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#define NON -1 /* まだ置いてない /*
#define N0 0 /* 基準値 /*
#define N1 1 /* 単位の数 /*
#define N2 2 /* N1 + N1 /*
#define N5 5 /* ペントミノを構成する座標の数 /*
#define N7 7 /* ペントミノの数 /*
#define N8 8 /* ペントミノの回転、反転などの数 /*
#define B6 6 /* B7 - N1 /*
#define B7 7 /* 盤(ペースを詰める正方形)の1辺の長さ /*
typedef struct {
    int x; /* X /*
    int y; /* Y /*
} XY;
XY pen2[N7][N8][N5]; /* 回転、反転したペントミノの座標 /*
int nPen2[N7]; /* 鏡像の個数 /*
int ban[B7][B7]; /* 盤(正方形) /*
int used[N7]; /* ペントミノを使用できる個数 /*
/******************************************************************************
機能:qsort用比較関数
引数:
  a : 比較対象a
  b : 比較対象b
戻り値:
  -1 : a <  b
  0  : a == b
  1  : a >  b
*****************************************************************************/*
int cmp(const void *a , const void *b)
{
    if         (((XY *)a)->y <  ((XY *)b)->y) return(-N1);
    else     if(((XY *)a)->y == ((XY *)b)->y) {
        if     (((XY *)a)->x <  ((XY *)b)->x) return(-N1);
        else if(((XY *)a)->x == ((XY *)b)->x) return(N0);
        else                                  return(N1);
    }
    else                                      return(N1);
}
/******************************************************************************
機能:初期化
引数:なし
戻り値:なし
*****************************************************************************/*
void init(void)
{
    int x , y; /* 座標 /*
    int i , j , k , l; /* ループの制御変数 /*
    int n; /* 鏡像の個数 /*
    int xmin , ymin; /* 原点位置 /*
    static XY pen1[N7][N5] = { /* ペントミノの座標の定義 /*
        0,0 ,  0,1 , 0,2 , 0,3 ,  0,4 , /* | /*
        0,0 ,  1,0 , 0,1 , 0,2 ,  0,3 , /* 」 /*
        0,0 ,  0,1 , 0,2 , 1,2 ,  0,3 , /* ト /*
        0,0 , -1,1 , 0,1 , 1,1 ,  0,2 , /* + /*
        0,0 ,  1,0 , 0,1 , 0,2 , -1,2 , /* Z /*
        0,0 ,  1,0 , 2,0 , 0,1 ,  0,2 , /* L /*
        0,0 ,  1,0 , 2,0 , 0,1 ,  2,1   /* 凹 /* };
    static XY dxy[N8][N2] = { /* 鏡像パターン /*
        1,0,0,1 , -1, 0,0,1 , 1,0, 0,-1 , -1, 0, 0,-1 ,
        0,1,1,0 ,  0,-1,1,0 , 0,1,-1, 0 ,  0,-1,-1, 0 };
    for(y = N0 ; y < B7 ; y++) { /* 盤を初期化 /*
        for(x = N0 ; x < B7 ; x++) ban[y][x] = NON;
    }
    for(y = N1 ; y < N7 ; y += N2) { /* 盤を初期化(置けない場所) /*
        for(x = N1 ; x < N7 ; x += N2) ban[y][x] = N7;
    }
    for(i = 0 ; i < N7 ; i++) { /* 鏡像生成 /*
        n = 0;
        for(j = N0 ; j < N8 ; j++) { /* 鏡像8パターン /*
            ymin = N7;
            for(k = N0 ; k < N5 ; k++) { /* 回転・反転後の座標 /*
                pen2[i][n][k].x = pen1[i][k].x * dxy[j][N0].x +
                                  pen1[i][k].y * dxy[j][N0].y;
                pen2[i][n][k].y = pen1[i][k].x * dxy[j][N1].x +
                                  pen1[i][k].y * dxy[j][N1].y;
                if(pen2[i][n][k].y < ymin) {
                    ymin = pen2[i][n][k].y;
                    xmin = pen2[i][n][k].x;
                }
                else if(pen2[i][n][k].y == ymin) {
                    if(pen2[i][n][k].x < xmin) xmin = pen2[i][n][k].x;
                }
            } /* 原点移動 /*
            for(k = N0 ; k < N5 ; k++) {
                pen2[i][n][k].x -= xmin;
                pen2[i][n][k].y -= ymin;
            }
            qsort(pen2[i][n] , N5 , sizeof(XY) , cmp); /* 座標順に並べる /*
            for(k = N0 ; k < n ; k++) { /* 既出パターンかどうかのチェック /*
                for(l = N0 ; l < N5 ; l++) {
                    if(pen2[i][n][l].x != pen2[i][k][l].x) break;
                    if(pen2[i][n][l].y != pen2[i][k][l].y) break;
                }
                if(l == N5) break;
            }
            if(k == n) n++; /* 初出パターンならカウントアップ /*
        }
        nPen2[i] = n; /* 鏡像の個数を登録 /*
    }
}
/******************************************************************************
機能:解答表示
引数:なし
戻り値:なし
*****************************************************************************/*
void disp(void)
{
    int  x , y;
    printf("-------\n");
    for(y = N0 ; y < B7 ; y++) {
        for(x = N0 ; x < B7 ; x++) {
            if(ban[y][x] == N7) printf("*");
            else                printf("%d" , ban[y][x]);
        }
        printf("\n");
    }
}
/******************************************************************************
機能:再帰呼び出し関数
引数:
  x : X座標
  y : Y座標
戻り値:なし
*****************************************************************************/*
void sub(int x , int y)
{
    int i , j , k; /* ループの制御変数 /*
    int x2 , y2; /* 座標 /*
    if(y == B7) { /* 全部置いた場合 /*
        if(ban[N0][N0] > ban[N0][B6]) return; /* 鏡像チェック /*
        if(ban[N0][N0] > ban[B6][N0]) return;
        if(ban[N0][N0] > ban[B6][B6]) return;
        if(ban[B6][N0] > ban[N0][B6]) return;
        if(ban[N0][N0] == ban[B6][N0] && ban[N0][B6] > ban[B6][B6]) return;
        disp(); /* 鏡像でなければ解答表示 /*
        return;
    }
    if(x == B7) { /* 右端に達した場合 /*
        x = N0;
        y++;
    }
    if(ban[y][x] != NON) sub(x + N1 , y);
    for(i = N0 ; i < N7 ; i++) {
        if(used[i] == N0) continue; /* 使用済みならスキップ /*
        used[i]--; /* 1個使用済みにする /*
        for(j = N0 ; j < nPen2[i] ; j++) {
             for(k = N0 ; k < N5 ; k++) { /* 置けるかどうかのチェック /*
                 x2 = x + pen2[i][j][k].x;
                 if(x2 < N0 || x2 > B6) break; /* ペントミノが正方形の外 /*
                 y2 = y + pen2[i][j][k].y;
                 if(y2 < N0 || y2 > B6) break;
                 if(ban[y2][x2] != NON) break;
             }
             if(k != N5) continue; /* 置けないならスキップ /*
             for(k = N0 ; k < N5 ; k++) {
                 ban[y + pen2[i][j][k].y][x + pen2[i][j][k].x] = i; /* 置く /*
             }
             sub(x + N1 , y); /* 次の座標(右へ1つ)へ /*
             for(k = N0 ; k < N5 ; k++) {
                 ban[y + pen2[i][j][k].y][x + pen2[i][j][k].x] = NON; /* 取る /*
             }
        }
        used[i]++; /* 1個未使用に戻す /*
    }
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:0
*****************************************************************************/*
int main(void)
{
    int i;
    init(); /* 初期化 /*
    for(i = N0 ; i < N7 ; i++) used[i] = N1;
    for(i = N0 ; i < N7 ; i++) { /* 1個追加するペントミノの番号 /*
        used[i]++; /* 1個追加 /*
        sub(N0 , N0);
        used[i]--; /* 元に戻す /*
    }
    return(EXIT_SUCCESS);
}


第155回(2月)十五長方形合流記

/******************************************************************************
Cマガジン2003年11月号 電脳クラブ(十五長方形合流記)
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define N0 0     /* 基準値 /*
#define N1 1     /* 単位の数 /*
#define N2 2     /* N1 + N1 /*
#define NON -1   /* 未使用 /*
#define MOJI 'A' /* 解答例表示の文字 /*
static int rect[][N2] = { /* 長方形の定義 /*
    13,60 , 15,37 , 16,41 , 17,51 , 18,48 ,
    19,56 , 20,44 , 21,29 , 23,32 , 24,31 ,
    25,27 , 26,61 , 28,40 , 30,43 , 42,47 };
int *square;    /* 正方形 NON:置かれてない、>NON:長方形が置かれている /*
int *sRect;     /* 長方形の状態 NON:未使用、N0:XY反転、N1:そのまま /*
int count = N0; /* 解答 /*
int nRect;      /* 長方形の数 /*
int len;        /* 正方形の辺の長さ /*
int len1;       /* len - N1 /*
int zCorner1 , zCorner2;    /* 正方形の角 /*
int last_no;    /* 最後に置いた長方形の番号 /*
int *unused;    /* 未使用の長方形の番号一覧 /*
/******************************************************************************
機能:解答例表示
引数:なし
戻り値:なし
*****************************************************************************/*
void disp(void)
{
    int i;     /* ループの制御変数 /*
    int x , y; /* 座標 /*
    int t1;    /* 一時変数 /*
    int no;    /* 長方形の番号 /*
    printf("解答例\n");
    for(y = N0 ; y < len ; y++) {
        t1 = y * len;
        x = N0;
        while(x < len) {
            no = square[t1 + x]; /* その場所の長方形の番号(左端) /*
            for(i = N0 ; i < rect[no][sRect[no]] ; i++) {
                printf("%c" , MOJI + no); /* X方向の長さ分、描画する /*
                x++;
            }
        }
        printf("\n");
    }
    printf("\n");
    for(i = N0 ; i < nRect ; i++) { /* 凡例 /*
        printf("%c : %3d * %3d\n" , MOJI + i , rect[i][N0] , rect[i][N1]);
    }
}
/******************************************************************************
機能:再帰呼び出し関数(長方形を1個置く)
引数:
 xp : X座標
  yp : Y座標
  n  : これまでに置いた長方形の個数
戻り値:なし
*****************************************************************************/*
void sub(int xp , int yp , int n)
{
    int i;         /* ループの制御変数 /*
    int x , y;     /* 座標 /*
    int idx , idy; /*  /*
    int n2;        /*  /*
    int t1;        /* 一時変数 /*
    int no;        /* 長方形の番号 /*
    if(n == nRect) { /* 全ての長方形を置いた場合 /*
        if(square[zCorner2] < square[zCorner1]) return; /* 鏡像チェック /*
        if(last_no < square[zCorner1]) return; /* 鏡像チェック /*
        if(count == N0) disp(); /* 1個目なら解答例を表示 /*
        count++; /* カウントアップ /*
        return;
    }
    if(xp == len) { /* 右端に達した /*
        if(yp == N0 && last_no < square[zCorner1]) return;
        xp = N0;
        yp++;
    }
    no = square[yp * len + xp]; /* その場所の状態 /*
    if(no != NON) { /* 既に長方形が置かれている /*
        sub(xp + rect[no][sRect[no]] , yp , n); /* 次へ /*
        return;
    }
    n2 = n + N1; /* 今回置いた後の長方形の数 /*
    for(idy = N0 ; idy < N2 ; idy++) { /* NO:そのまま、N1:XY反転 /*
        idx = N1 - idy;
        for(i = N0 ; i < nRect - n ; i++) {
            no = unused[i];
            unused[i] = unused[nRect - n2];
            for(y = N0 ; y < rect[no][idy] ; y++) { /* 長方形を置けるか? /*
                t1 = (yp + y) * len + xp;
                for(x = N0 ; x < rect[no][idx] ; x++) {
                    if(square[t1++] != NON) break; /* 既に長方形あり /*
                }
                if(x != rect[no][idx]) break;
            }
            if(y == rect[no][idy]) { /* 長方形を置ける場合 /*
                for(y = N0 ; y < rect[no][idy] ; y++) {
                     square[(yp + y) * len + xp] = no; /* 長方形を置く /*
                }
                sRect[no] = idx; /* 使用済みにする /*
                last_no = no;
                sub(xp + rect[no][idx] , yp , n2);
                sRect[no] = NON; /* 未使用に戻す /*
                for(y = N0 ; y < rect[no][idy] ; y++) {
                    square[(yp + y) * len + xp] = NON; /* 長方形を取る /*
                }
            }
            unused[i] = no;
        }
    }
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:0
*****************************************************************************/*
int main(void)
{
    int x , y;  /* 座標 /*
    int i;      /* ループの制御変数 /*
    int s = N0; /* 正方形の面積 /*
    int t1;     /* 一時変数 /*
    int no;
    nRect = sizeof(rect) / sizeof(rect[N0]); /* 長方形の数を求める /*
    for(i = N0 ; i < nRect ; i++) { /* 正方形の面積を求める /*
        s += rect[i][N0] * rect[i][N1];
    }
    len = (int)sqrt((double)s); /* 辺の長さを求める /*
    if(len * len != s) {
        fprintf(stderr , "長方形を合流しても正方形になりません\n");
        exit(EXIT_FAILURE);
    }
    len1 = len - N1;
    zCorner1 = N0 * len + N0;   /* 正方形の角 /*
    zCorner2 = len1 * len + N0; /* 正方形の角 /*
    square = (int *)malloc(sizeof(int) * len * len); /* 正方形メモリ確保 /*
    if(square == NULL) {
        fprintf(stderr , "mallocエラー at %d\n" , __LINE__);
        exit(EXIT_FAILURE);
    }
    sRect = (int *)malloc(sizeof(int) * nRect); /* 状態メモリ確保 /*
    if(sRect == NULL) {
        free(square);
        fprintf(stderr , "mallocエラー at %d\n" , __LINE__);
        exit(EXIT_FAILURE);
    }
    unused = (int *)malloc(sizeof(int) * nRect); /* 未使用長方形メモリ確保 /*
    if(unused == NULL) {
        free(square);
        free(sRect);
        fprintf(stderr , "mallocエラー at %d\n" , __LINE__);
        exit(EXIT_FAILURE);
    }
    for(i = N0 ; i < nRect ; i++) {
        sRect[i] = NON; /* 全て未使用にする /*
        unused[i] = i;
    }
    for(y = N0 ; y < len ; y++) {
        t1 = y * len;
        for(x = N0 ; x < len ; x++) square[t1++] = NON; /* 置いてない状態 /*
    }
    for(i = N0 ; i < nRect ; i++) { /* 1個目の長方形(向き固定) /*
        no = unused[i];
        unused[i] = unused[nRect - N1];
        for(y = N0 ; y < rect[no][N0] ; y++) square[y * len] = no; /* 置く /*
        sRect[no] = N1; /* 使用済みにする /*
        sub(rect[no][N1] , N0 , N1); /* 1個置いて次へ /*
        sRect[no] = NON; /* 未使用に戻す /*
        for(y = N0 ; y < rect[no][N0] ; y++) square[y * len] = NON; /* 取る /*
        unused[i] = no;
    }
    printf("解答:%d通り\n" , count);
    free(unused);
    free(sRect);
    free(square);
    return(EXIT_SUCCESS);
}


第154回(1月)どろけい

/******************************************************************************
Cマガジン2003年10月電脳クラブ出題(どろけい)
*****************************************************************************/*
#include <stdio.h>
#include <stdlib.h>
#define OFF 0 /* オフ /*
#define ON  1 /* オン /*
#define N0 0 /* 基準値 /*
#define N1 1 /* 単位の数 /*
#define N2 2 /* N1 + N1 /*
#define N3 3 /* 一つの座標から遷移でいる座標数の最大 /*
#define N6 6 /* 座標の数 /*
#define DAN 3 /* 解答例で、段の数 /*
#define DAN0 0 /* 上段 /*
#define DAN1 1 /* 中段 /*
#define DAN2 2 /* 下段 /*
#define POS0 0 /* 上&中央 /*
#define POS1 1 /* 中&左 /*
#define POS2 2 /* 下&左 /*
#define POS3 3 /* 下&右 /*
#define POS4 4 /* 中&右 /*
#define POS5 5 /* 中&中 /*
#define N72 72 /* N6 * N6 * N2(刑事、泥棒を合わせた局面の数) /*
#define NOLINK -1 /* リンクなし /*
#define KPOS POS5 /* 刑事の初期座標 /*
#define DPOS POS4 /* 泥棒の初期座標 /*
#define KEIJI  "刑" /* 手順表示(刑事のシンボル) /*
#define DOROBO "泥" /* 手順表示(泥棒のシンボル) /*
#define HOKAKU "捕" /* 手順表示(捕獲場所のシンボル) /*
#define NON    "○" /* 手順表示(刑事も泥棒もいない場所) /*
#define SPACE  " " /* 手順表示(空白) /*
#define YA     "→" /* 手順表示(矢印) /*
#define TE "手" /* 正解表示で使用する単位 /*
static int link[][N3] = { /* 座標間のリンク情報 /*
    POS1 , POS5 , POS4   ,
    POS0 , POS2 , NOLINK ,
    POS1 , POS5 , POS3   ,
    POS2 , POS5 , POS4   ,
    POS0 , POS3 , NOLINK ,
    POS0 , POS2 , POS3   };
int min = N1; /* 最小手数(この範囲で探索する) /*
int fPass[N6][N6]; /* そのパターンを通過済みどうかのフラグ /*
int tejun[N72]; /* 最善手順 /*
int moveKei(int , int , int); /* プロトタイプ宣言 /*
void moveKei2(int , int , int); /* プロトタイプ宣言 /*
/******************************************************************************
機能:手順を表示する(1座標ずつ)
引数:
  (1) keiPos  : 刑事の座標
  (2) doroPos : 泥棒の座標
  (3) pos       : 結果を表示する座標
戻り値:なし
*****************************************************************************/*
void disp1(int keiPos , int doroPos , int pos)
{
    if     (keiPos  == pos && doroPos == pos)  printf(HOKAKU); /* 捕獲 /*
    else if(keiPos  == pos)                    printf(KEIJI);  /* 刑事 /*
    else if(doroPos == pos)                    printf(DOROBO); /* 泥棒 /*
    else                                       printf(NON);    /* いない /*
}
/******************************************************************************
機能:手順を表示する(1段ずつ)
引数:
  (1) keiPos  : 刑事の座標
  (2) doroPos : 泥棒の座標
  (3) dan       : 段
  (4) x         : 横方向
戻り値:なし
*****************************************************************************/*
void dispDan(int keiPos , int doroPos , int dan , int x)
{
    if     (dan == DAN0) printf(SPACE); /* 上段 /*
    else if(dan == DAN2) printf(" "); /* 下段 /*
    if(dan == DAN0) { /* 上段 /*
        disp1(keiPos , doroPos , POS0);
    }
    else if(dan == DAN1) { /* 中段 /*
        disp1(keiPos , doroPos , POS1);
        disp1(keiPos , doroPos , POS5);
        disp1(keiPos , doroPos , POS4);
    }
    else if(dan == DAN2) { /* 下段 /*
        disp1(keiPos , doroPos , POS2);
        disp1(keiPos , doroPos , POS3);
    }
    if(x < min) {
        if     (dan == DAN0) { /* 上段 /*
            printf(SPACE);
            printf(SPACE);
        }
        else if(dan == DAN2) { /* 下段 /*
            printf(" ");
            printf(SPACE);
        }
        else if(dan == DAN1) printf(YA); /* 中段 /*
    }
    else printf("\n"); /* 右端では改行 /*
}
/******************************************************************************
機能:手順を表示する(全体)
引数:なし
戻り値:なし
*****************************************************************************/*
void dispTejun(void)
{
    int i , j;
    int keiPos , doroPos;
    printf("\n");
    for(i = N0 ; i < DAN ; i++) { /* 手順表示 /*
        keiPos  = KPOS; /* 刑事、泥棒の座標を初期化 /*
        doroPos = DPOS;
        for(j = N0 ; j < min ; j += N2) {
            dispDan(keiPos , doroPos , i , j);
            keiPos = tejun[j]; /* 最善手順で刑事を動かす /*
            dispDan(keiPos , doroPos , i , j + N1);
            doroPos = tejun[j + N1]; /* 最善手順で泥棒を動かす /*
        }
    }
}
/******************************************************************************
機能:泥棒を動かす(最短手順探索用)
引数:
  (1) keiPos : 刑事の座標
  (2) doroPos : 泥棒の座標
  (3) n : ここまでの手数(0オリジン)
戻り値:この局面で泥棒が最善手を尽くした場合の手数
*****************************************************************************/*
int moveDoro(int keiPos , int doroPos , int n)
{
    int i; /* ループの制御変数 /*
    int nextPos; /* 次の座標 /*
    int bestTe = N0 , te;
    if(keiPos == doroPos) return(n); /* 捕獲されたら探索中止 /*
    for(i = N0 ; i < N3 ; i++) {
        nextPos = link[doroPos][i]; /* 次の手(候補) /*
        if(nextPos == NOLINK) continue; /* リンク先がない /*
        if(keiPos == nextPos) continue; /* そこには刑事がいた /*
        te = moveKei(keiPos , nextPos , n + N1); /* 手数 /*
        if(te > bestTe) bestTe = te; /* より多い手数を最善手数とする /*
    }
    return(bestTe); /* 手数を返す /*
}
/******************************************************************************
機能:刑事を動かす(最短手順探索用)
引数:
  (1) keiPos : 刑事の座標
  (2) doroPos : 泥棒の座標
  (3) n : ここまでの手数(0オリジン)
戻り値:この局面で刑事が最善手を尽くした場合の手数
*****************************************************************************/*
int moveKei(int keiPos , int doroPos , int n)
{
    int i; /* ループの制御変数 /*
    int nextPos; /* 次の座標 /*
    int bestTe = N72 , te;
    if(min < n) return(n);
    for(i = N0 ; i < N3 ; i++) {
        nextPos = link[keiPos][i]; /* 次の手(候補) /*
        if(nextPos == NOLINK) continue; /* リンク先がない /*
        if(fPass[nextPos][doroPos] == ON) continue; /* 通過済みの局面 /*
        fPass[nextPos][doroPos] = ON; /* 通過済みとする /*
        te = moveDoro(nextPos , doroPos , n + N1); /* 手数 /*
        fPass[nextPos][doroPos] = OFF; /* 通過済みを解除する /*
        if(te < bestTe) bestTe = te; /* より少ない手数を最善手数とする /*
    }
    return(bestTe); /* 手数を返す /*
}
/******************************************************************************
機能:泥棒を動かす(手順表示用)
引数:
  (1) keiPos : 刑事の座標
  (2) doroPos : 泥棒の座標
  (3) n : ここまでの手数(0オリジン)
戻り値:この局面で泥棒が最善手を尽くした場合の手数
*****************************************************************************/*
void moveDoro2(int keiPos , int doroPos , int n)
{
    int i; /* ループの制御変数 /*
    int nextPos; /* 次の座標 /*
    int bestTe = N0 , te;
    int rTe[N3];
    if(keiPos == doroPos) {
        dispTejun();
        return; /* 捕獲されたら手順を表示して探索中止 /*
    }
    for(i = N0 ; i < N3 ; i++) {
        rTe[i] = N0;
        nextPos = link[doroPos][i]; /* 次の手(候補) /*
        if(nextPos == NOLINK) continue; /* リンク先がない /*
        if(keiPos == nextPos) continue; /* そこには刑事がいた /*
        rTe[i] = te = moveKei(keiPos , nextPos , n + N1); /* 手数 /*
        if(te > bestTe) bestTe = te; /* より多い手数を最善手数とする /*
    }
    for(i = N0 ; i < N3 ; i++) {
        if(rTe[i] != bestTe) continue;
        tejun[n] = link[doroPos][i]; /* 最善手順を登録する /*
        moveKei2(keiPos , tejun[n] , n + N1); /* 次の刑事移動へ /*
    }
}
/******************************************************************************
機能:刑事を動かす(手順表示用)
引数:
  (1) keiPos : 刑事の座標
  (2) doroPos : 泥棒の座標
  (3) n : ここまでの手数(0オリジン)
戻り値:この局面で刑事が最善手を尽くした場合の手数
*****************************************************************************/*
void moveKei2(int keiPos , int doroPos , int n)
{
    int i; /* ループの制御変数 /*
    int nextPos; /* 次の座標 /*
    int bestTe = N72 , te;
    int rTe[N3];
    if(min < n) return;
    for(i = N0 ; i < N3 ; i++) {
        rTe[i] = N72;
        nextPos = link[keiPos][i]; /* 次の手(候補) /*
        if(nextPos == NOLINK) continue; /* リンク先がない /*
        if(fPass[nextPos][doroPos] == ON) continue; /* 通過済みの局面 /*
        fPass[nextPos][doroPos] = ON; /* 通過済みとする /*
        rTe[i] = te = moveDoro(nextPos , doroPos , n + N1); /* 手数 /*
        fPass[nextPos][doroPos] = OFF; /* 通過済みを解除する /*
        if(te < bestTe) bestTe = te; /* より少ない手数を最善手数とする /*
    }
    for(i = N0 ; i < N3;  i++) {
        if(rTe[i] != bestTe) continue;
        tejun[n] = link[keiPos][i]; /* 最善手順を登録する /*
        moveDoro2(tejun[n] , doroPos , n + N1); /* 次の泥棒移動へ /*
    }
}
/******************************************************************************
機能:メイン
引数:なし
戻り値:0
*****************************************************************************/*
int main(void)
{
    int i , j; /* ループの制御変数 /*
    for(i = N0 ; i < N6 ; i++) { /* 通過フラグを初期化 /*
        for(j = N0 ; j < N6 ; j++) fPass[i][j] = OFF;
    }
    while(moveKei(KPOS , DPOS , N0) != min) min += N2; /* 解答を探索する /*
    printf("最短手順:%d手\n" , min); /* 解答表示 /*
    moveKei2(KPOS , DPOS , N0); /* 手順表示 /*
    return(EXIT_SUCCESS);
}