AOJ0243 Filling Game
問題リンク Filling Game
- 解法
深さ優先探索+枝刈りで解きました。
まず、最初から隣接している同色のパネルはくっつけてグループにしておきます。そして、グループ間の隣接リストを作っておきます。
1度くっついたグループは以降離れることはないので、boolean配列に、左上の頂点を含むグループとくっついたかどうかを格納しておきます。既にくっついたグループに隣接しているものが次にくっつけるグループの候補になります。全部のグループがくっついたら終了です。
計算時間が気になります。少し自分で最悪になりそうなケースを作ってみると
10 10
B R G R B R G B R G
G B R B G B R G B R
R G B G B G B R G B
B R G R G R G B R G
G B R G R G B R G B
R G B R G B R B R G
B R G B R G B R G B
G B R G B R G B R G
B R G B G B R G B R
R B R G B G B R G B
答えは19です。なので最適解は20くらいまであり得そうです。つまり、再帰で20回潜ることになります。左上のパネルの色を指定するとき、同じ色を連続して指定する意味はないので塗る色の候補は2色あることになります。深さ20で枝が2つずつ分かれると100万回近い再帰処理を行うことになります。
よって自明な枝刈りだけでは心配になります。
グループとその隣接関係をグラフに考えると、一番到達するのが離れているノードまでは、必ずその距離だけ塗る回数が必要になるので枝刈りに使えます。現在の深さdepth、暫定最適解res、最遠距離maxとすると、
res <= depth + max
のとき、解の改善が見込めないので枝刈りできます。
- ソース
import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Set; //Filling Game public class AOJ0243 { int h, w, N, res; int[][] a, id; int[][] d = {{-1,0},{0,1},{1,0},{0,-1}}; Set<Integer>[] adj; int[] col, bfs; boolean[] u; boolean ok(int i, int j){ return 0<=i&&i<h&&0<=j&&j<w; } void fill(int i, int j, int ID, int col){ a[i][j] = -1; id[i][j] = ID; for(int k=0;k<4;k++){ int ni = i+d[k][0], nj = j+d[k][1]; if(ok(ni, nj)&&a[ni][nj]==col)fill(ni, nj, ID, col); } } @SuppressWarnings("unchecked") void f(int depth, int sum){ if(sum==N){ res = depth; return; } Arrays.fill(bfs, 1<<29); Queue<Integer> q = new LinkedList<Integer>(); for(int i=0;i<N;i++)if(u[i]){ bfs[i] = 0; q.add(i); } int max = 0; while(!q.isEmpty()){ int v = q.poll(); for(int x:adj[v]){ if(bfs[x]==1<<29){ bfs[x] = bfs[v]+1; q.add(x); max = Math.max(max, bfs[x]); } } } if(res<=depth+max)return; Set<Integer>[] nx = new Set[3]; for(int i=0;i<3;i++)nx[i]=new HashSet<Integer>(); for(int i=0;i<N;i++)if(u[i]){ for(int x:adj[i]){ if(!u[x])nx[col[x]].add(x); } } for(int c=0;c<3;c++)if(!nx[c].isEmpty()){ for(int x:nx[c])u[x]=true; f(depth+1, sum+nx[c].size()); for(int x:nx[c])u[x]=false; } } @SuppressWarnings("unchecked") void run(){ Scanner sc = new Scanner(System.in); adj = new Set[150]; for(;;){ w = sc.nextInt(); h = sc.nextInt(); if((w|h)==0)break; a = new int[h][w]; id = new int[h][w]; for(int i=0;i<h;i++)for(int j=0;j<w;j++){ char c = sc.next().charAt(0); a[i][j] = c=='R'?0:c=='G'?1:2; } N = 0; col = new int[h*w]; for(int i=0;i<h;i++)for(int j=0;j<w;j++)if(a[i][j]!=-1){ col[N] = a[i][j]; fill(i, j, N, a[i][j]); N++; } for(int i=0;i<N;i++)adj[i] = new HashSet<Integer>(); for(int i=0;i<h;i++)for(int j=0;j<w;j++){ for(int k=0;k<4;k++){ int ni = i+d[k][0], nj = j+d[k][1]; if(ok(ni, nj)&&id[i][j]!=id[ni][nj])adj[id[i][j]].add(id[ni][nj]); } } res = N; u = new boolean[N]; u[0] = true; bfs = new int[N]; f(0, 1); System.out.println(res); } } public static void main(String[] args) { new AOJ0243().run(); } }