AOJ2040 Sort the Panels

問題リンク Sort the Panels

  • 概要

N個の'W'と'B'からなるパネルの順列S, Tが与えられる。次のルールに従ってSをTへ変化させるのにかかるコストを最小化せよ。
機械が1台ある。機械は任意のパネルへ移動してマークをつける。続けてもう1か所別のパネルへ移動してマークをつけ、マークをつけた箇所のパネルをスワップする。コストは機械が移動した距離である。また、機械の初期位置は任意に選ぶことができる。機械を初期配置に置くことにコストはかからない。
SからTの順列を得る手順が必ず存在することが保証されている。
2 <= N <= 16

  • 解法

(パネルの状態、機械の位置)をノードとしたダイクストラで解きます。パネルの状態はB, Wを0, 1と思えば1つの整数値で表すことができます。
(s, p)からの次のノードは1個目のマークをつける箇所a, 2個目のマークをつける箇所bを選び、sのaビット目とbビット目をスワップした状態ns、位置bです。移動コストはpからaへ、aからbへ動く距離の和なので、d[s][p]+abs(p-a)+abs(a-b)です。
スワップする個所a, bは、揃っている箇所を崩すと絶対損になるだろうと予想して、sのaビット目とbビット目がTのaビット目とbビット目と両方とも異なっている場合のみ、スワップを行うということにしています。この予想はどうやら当たりだったようです。

  • ソース
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

//Sort the Panels
public class AOJ2040 {

	int[][] dist;
	int n, M = 20, INF = 1<<29, S, G;
	void run(){
		Scanner sc = new Scanner(System.in);
		for(;;){
			n = sc.nextInt();
			if(n==0)break;
			char[] t1 = sc.next().toCharArray(), t2 = sc.next().toCharArray();
			S = 0; G = 0;
			for(int i=0;i<n;i++){
				S+=t1[i]=='W'?1<<i:0; G+=t2[i]=='W'?1<<i:0;
			}
			dist = new int[1<<n][n];
			for(int[]a:dist)Arrays.fill(a, INF);
			PriorityQueue<Integer> q = new PriorityQueue<Integer>(n, new Comparator<Integer>() {
				public int compare(Integer o1, Integer o2) {
					return dist[o1/M][o1%M]-dist[o2/M][o2%M];
				}
			});
			for(int i=0;i<n;i++){
				dist[S][i] = 0; q.add(S*M+i);
			}
			while(!q.isEmpty()){
				int V = q.poll();
				int s = V/M, p = V%M;
				if(s==G){
					System.out.println(dist[s][p]); break;
				}
				for(int a=0;a<n;a++){
					int pa = s&(1<<a);
					if(pa==(G&(1<<a)))continue;
					for(int b=0;b<n;b++){
						if(a==b||(s&(1<<b))==(G&(1<<b)))continue;
						int w = dist[s][p]+Math.abs(p-a)+Math.abs(a-b);
						int pb = s&(1<<b), ns = s;
						if(pa>0)ns|=1<<b;
						else ns&=~(1<<b);
						if(pb>0)ns|=1<<a;
						else ns&=~(1<<a);
						if(w<dist[ns][b]){
							dist[ns][b] = w; q.add(ns*M+b);
						}
					}
				}
			}
		}
	}

	public static void main(String[] args) {
		new AOJ2040().run();
	}
}