AOJ1245 Gap
問題リンク Gap
- 概要
Gapというカードゲームをやる。これをクリアするまでにかかる最短の手数を答えよ。
Gapは4*8のボードを使い、1〜4の柄それぞれについて1〜7の数が1枚ずつある。ボードには常に4か所の空欄がある。
1手につき、空欄にカードを移動することができる。移動させられるカードは、その空欄の左隣りのカードと柄一緒で数が1だけ大きいカードだけである。つまり、空欄の左隣りも空欄であったり、左隣りのカードの数が7だったりすると、その空欄にカードを移動させられない。
Gapの初期状態が与えられる。初期状態から、数が1であるカードを左の列に移動させたところからゲームが始まる。この移動は手数のカウントに含まない。
クリアできる手順が無い場合は-1と答えよ。
- 解法
方針はメモ化再帰です。
カード1枚は2文字の文字列で表せます(空欄は"00"としました)。これを32個並べて長さ64の文字列でゲームの状態を表すことにします。Mapにはキーがマップの状態で、値はその状態に至る最短ステップです。
再帰の各ステップでは空欄の場所を探します。空欄の場所に来ることができるカードが無いならばそこは無視します。あれば、そのカードを移動させるために、ボードの中から探します。ここの処理を少し速くするために、大きさ48の配列に、各カードがボードのどこにあるかを覚えておくようにしました(カード15はpos[15]に入っているand so on.)。
例によって例のごとく、メモリが足りるか不安でしたが何とかなりました。
- ソース
import java.util.HashMap; import java.util.Map; import java.util.Scanner; //Gap public class AOJ1245 { String trans(){ StringBuilder sb = new StringBuilder(); for(int i=0;i<4;i++)for(int j=0;j<8;j++){ sb.append(a[i][j]==0?"00":a[i][j]); } return sb.toString(); } int min, INF = 1<<29; int[][] a; int[] pos; String g = "1112131415161700212223242526270031323334353637004142434445464700"; Map<String, Integer> ref; void dfs(int d){ String r = trans(); if(min<=d||ref.containsKey(r)&&ref.get(r)<=d)return; if(r.equals(g)){ min = d;return; } ref.put(r, d); for(int i=0;i<4;i++)for(int j=1;j<8;j++){ if(a[i][j]!=0||a[i][j-1]==0||a[i][j-1]%10==7)continue; int x = a[i][j-1]+1; int p = pos[x]; a[p/8][p%8] = 0; a[i][j] = x; pos[x] = i*8+j; dfs(d+1); a[p/8][p%8] = x; a[i][j] = 0; pos[x] = p; } } void run(){ Scanner sc = new Scanner(System.in); int T = sc.nextInt(); while(T--!=0){ min = INF; a = new int[4][8]; for(int i=0;i<4;i++)for(int j=1;j<8;j++){ a[i][j]=sc.nextInt(); } for(int i=0;i<4;i++)for(int j=1;j<8;j++){ if(a[i][j]%10==1){ int t = a[i][j]/10-1; a[t][0] = a[i][j]; a[i][j] = 0; } } pos = new int[48]; for(int i=0;i<4;i++)for(int j=0;j<8;j++){ if(a[i][j]==0)continue; pos[a[i][j]] = i*8+j; } ref = new HashMap<String, Integer>(); dfs(0); System.out.println(min==INF?-1:min); } } public static void main(String[] args) { new AOJ1245().run(); } }