AOJ2366 Elevator

問題リンク Elevator

  • 解法

シミュレーションのようなDPのような解き方です。
エレベータの動かし方は、「荷物が存在する最上階の荷物を1つ下のフロアに最適に運ぶ」というのを繰り返せば答えとなります(解説の記憶が残っていました)。よって、ある解の荷物の集合Sは2^15通りしかないため、各Sについて事前に最低コスト(その階で荷物を搬出する最低回数)を求めておけば解けます。求め方ですが、Sを小さい方から求めていくとして、Sの部分集合かつ総重量がWを超えないものをsubとするとき、dp[S-sub] + 1の最低となるsubを見つければOKです。Sは小さい方から求めているのでS-subは既に求まっているはずです。
最初エレベータはm階にいますが、結局荷物の存在する最上階まで動かすため、そこへ移動するためのコストが解に加算されます。

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

//Elevator
public class AOJ2366 {

	int[] d;
	void run(){
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt(), m = sc.nextInt(), W = sc.nextInt(), K = 0;
		int[] w = new int[15], f = new int[15], s = new int[n+1];
		for(int i=1;i<=n;i++){
			int k = sc.nextInt();
			while(k--!=0){
				w[K] = sc.nextInt(); s[i]+=1<<K; f[K++] = i;
			}
		}
		int[] sum = new int[1<<K];
		boolean[] valid = new boolean[1<<K];
		for(int S=0;S<1<<K;S++){
			for(int j=0;j<K;j++)if(((S>>j)&1)>0){
				sum[S]+=w[j];
			}
			valid[S] = sum[S]<=W;
		}
		int[] min = new int[1<<K];
		Arrays.fill(min, K);
		min[0] = 0;
		for(int S=1;S<1<<K;S++){
			int res = K, sub = S;
			do{
				if(valid[sub]){
					res = Math.min(res, min[S-sub]+1);
				}
				sub = (sub-1)&S;
			}while(sub!=S);
			min[S] = res;
		}
		int res = f[K-1]-m;
		for(int i=f[K-1];i>1;i--){
			res+=2*(min[s[i]]-1)+1;
			s[i-1]|=s[i];
		}
		System.out.println(Math.max(res, 0));
	}

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