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 JAVA (judge verdict: Accepted)

import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.PriorityQueue;
import java.util.Scanner;
import java.util.TreeSet;

public class Main {
	
	public static class Segment{
		int length, color;

		public Segment(int length, int color) {
			super();
			this.length = length;
			this.color = color;
		}

		@Override
		public String toString() {
			return "Segment [length=" + length + ", color=" + color + "]";
		}
	}
	
	public static int remove(ArrayList<Segment> seg_list, int left, int right){
		int reduce = 0;
		
		while(left >= 0 && right < seg_list.size()){
			if(seg_list.get(left).color != seg_list.get(right).color){
				break;
			}
			
			final int len = seg_list.get(left).length + seg_list.get(right).length;
			if(len < 4){
				break;
			}
			
			left--;
			right++;
			reduce += len;
		}
		
		return reduce;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		while(true){
			final int n = sc.nextInt();
			
			if(n == 0){
				break;
			}
			
			ArrayList<Segment> seg_list = new ArrayList<Main.Segment>();
			for(int i = 0; i < n; i++){
				final int color = sc.nextInt();
				
				if(seg_list.isEmpty()){
					seg_list.add(new Segment(1, color));
				}else if(seg_list.get(seg_list.size() - 1).color == color){
					seg_list.get(seg_list.size() - 1).length++;
				}else{
					seg_list.add(new Segment(1, color));
				}
			}
			
			int min = n;
			
			for(int divide = 0; divide < seg_list.size(); divide++){
				if(seg_list.get(divide).length == 1){
					//
					//--xxxo---
					if(divide != 0 && seg_list.get(divide - 1).length >= 3){
						final int len = 1 + seg_list.get(divide - 1).length;
						min = Math.min(min, n - len - remove(seg_list, divide - 2, divide + 1));
					}
					
					//oxxx
					if(divide != seg_list.size() - 1 && seg_list.get(divide + 1).length >= 3){
						final int len = 1 + seg_list.get(divide + 1).length;
						min = Math.min(min, n - len - remove(seg_list, divide - 1, divide + 2));
					}
					
					//xxoxx
					if(divide != 0 && divide != seg_list.size() - 1 &&
							seg_list.get(divide - 1).color == seg_list.get(divide + 1).color){
						final int len = seg_list.get(divide - 1).length + seg_list.get(divide + 1).length + 1;
						
						if(len >= 4){
							min = Math.min(min, n - len - remove(seg_list, divide - 2, divide + 2));
						}
					}
				}else{
					//--xxxo---
					if(divide != 0 && seg_list.get(divide - 1).length >= 3){
						final int len = seg_list.get(divide - 1).length + 1;
						seg_list.get(divide).length--;
						min = Math.min(min, n - len - remove(seg_list, divide - 2, divide));
						seg_list.get(divide).length++;
					}
					
					//oxxx
					if(divide != seg_list.size() - 1 && seg_list.get(divide + 1).length >= 3){
						final int len = 1 + seg_list.get(divide + 1).length;
						seg_list.get(divide).length--;
						min = Math.min(min, n - len - remove(seg_list, divide, divide + 2));
						seg_list.get(divide).length++;
					}
				}
			}
			
			System.out.println(min);
		}
		
	}
}