Problem:
連鎖

問題

次のようなゲームがある．

あるキャラクターが縦 1 列に N 個並んでいる．これらのキャラクターの色は赤，青，黄のいずれかであり，初期状態で同じ色のキャラクターが4つ以上連続して並んでいることはない．プレーヤーは，ある位置のキャラクターを選び他の色に変更することができる．この操作により同じ色のキャラクターが4つ以上連続して並ぶとそれらのキャラクターは消滅する．キャラクターが消滅することにより新たに同じ色のキャラクターが4つ以上連続して並ぶとそれらのキャラクターも消滅し，同じ色のキャラクターが4つ以上連続して並んでいる箇所がなくなるまでこの連鎖は続く．このゲームの目的は，消滅しないで残っているキャラクター数をなるべく少なくすることである．

例えば，下図の左端の状態で，上から6番目のキャラクターの色を黄色から青に変更すると，青のキャラクターが5つ連続するので消滅し，最終的に3つのキャラクターが消滅せずに残る．

初期状態における N 個のキャラクターの色の並びが与えられたとき， 1箇所だけキャラクターの色を変更した場合の，消滅しないで残っているキャラクター数の最小値 M を求めるプログラムを作成せよ．

入力

入力は複数のデータセットからなる．各データセットは以下の形式で与えられる．

1行目はキャラクター数 N (1 ≤ N ≤ 10000) だけからなる．続く N 行には 1, 2, 3 のいずれか1つの整数が書かれており， i + 1 行目 (1 ≤ i ≤ N) は初期状態における上から i 番目のキャラクターの色を表す（1 は赤を，2 は青を，3は黄を表す）．

N が 0 のとき入力の終了を示す．データセットの数は 5 を超えない．

出力

データセットごとに, 消滅しないで残っているキャラクター数の最小値 M を１行に出力せよ．

入出力例

入力例

12
3
2
1
1
2
3
2
2
2
1
1
3
12
3
2
1
1
2
3
2
1
3
2
1
3
0

出力例

3
12

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C (judge verdict: Accepted)

#include <stdio.h>
#include <string.h>
int min(int a, int b)
{
    if (a > b){
        return (b);
    }
    return (a);
}

int switch_color(int *ball, int check, int col, int n, int *top, int *bottom)
{
    int i, j;
    
    *top = check;
    i = check - 1;
    while (i >= 0 && ball[i] == col){
        *top = i;
        i--;
    }
    
    *bottom = check;
    i = check + 1;
    while (i < n && ball[i] == col){
        *bottom = i;
        i++;
    }
    return(*bottom - *top + 1);
}

int chain(int *temp, int start, int end, int n)
{
    int i, j, k;
    int temp_2[30001];
    
    i = 0;
    while (i != n && n >= 0){
        for (j = 1; j <= 3; j++){
            if (temp[i] == j && switch_color(temp, i, j, n, &start, &end) >= 4){
                memset(temp_2, -1, sizeof(temp_2));
                n -= (end - start + 1);
                for (k = 0; k < n; k++){
                    if (k < start){
                        temp_2[k] = temp[k];
                    }
                    else {
                        temp_2[k] = temp[(end - start + 1) + k];
                    }
                }
                for (k = 0; k < n; k++){
                    temp[k] = temp_2[k];
                }
                i = 0;
            }
        }
        i++;
    }
    if (n < 0){
        return (0);
    }
    return (n);
}

int count(int check, int n, int *ball)
{
    int i, j;
    int top, bottom;
    int ans, comp;
    int temp[30001];
    
    ans = 100000;
    for (i = 1; i <= 3; i++){
        if (ball[check] != i){
            if (switch_color(ball, check, i, n, &top, &bottom) >= 4){
                ans = n - (bottom - top + 1);
                memset(temp, -1, sizeof(temp));
                for (j = 0; j < ans; j++){
                    if (j < top){
                        temp[j] = ball[j];
                    }
                    else {
                        temp[j] = ball[(bottom - top + 1) + j];
                    }
                }
                comp = 100000;
                if (temp[top - 1] == temp[top]){
                    comp = chain(temp, top, bottom, ans);
                }
                ans = min(comp, ans);
                if (ans == 0){
                    break;
                }
            }
            if (ans == 0){
                break;
            }
        }
    }
    if (ans == 100000){
        return (n);
    }
    return (ans);
}

int main(void)
{
    int ball[30001];
    int n;
    int i;
    int ans;
    
    while (1){
        scanf("%d", &n);
        
        if (n == 0){
            break;
        }
        
        memset(ball, -1, sizeof(ball));
        for (i = 0; i < n; i++){
            scanf("%d", &ball[i]);
        }
        
        ans = 100000;
        for (i = 0; i < n; i++){
            if (i == 0 || i == n - 1 || !(ball[i] == ball[i - 1] && ball[i] == ball[i + 1])){
                ans = min(ans, count(i, n, ball));
                if (ans == 0){
                    break;
                }
            }
        }
        
        printf("%d\n", ans);
    }
    
    return (0);
}