Problem:
Problem 08: Provident Housewife

主婦の琴子はこの不況のさなか食費を抑えることに闘志を燃やしていました。毎朝新聞広告を必ずチェック。買う物リストと最も安く売られているお店をしっかりリストアップして、お店をはしごしながら、エプロン・サンダルという戦闘スタイルで自転車をこぎこぎ買い物に行きます。

そこで夫のあなたは少しでも琴子の役に立とうと、得意のプログラミングで家計簿プログラムを作成することにしました。プログラムは、各スーパーに売っている品物の名前と値段（円）、琴子が必要な品物を入力し、全ての品物を集めるための最小金額を出力します。

琴子は家を出て必要な品物を集め、家に戻ってこなければなりません。
そこで、琴子を気遣ったあなたは、最小金額が同じになる買い物ルートが複数ある場合も考慮して、より距離が短くなるルートの距離を報告する機能を追加することにしました。従って、プログラムはスーパー・家間を繋ぐ道の情報も入力します。

スーパーの数を n とし、各スーパーにはそれぞれ 1 から n までの番号が割り当てられます。さらに琴子の家を 0 番とします。道の情報はこれらの番号のペアとその距離（整数）で与えられます。

Input

入力として複数のデータセットが与えられます。各データセットの形式は以下の通りです：

n　（スーパーの数：整数）

k1 name1 value1 name2 value2 . . . namek1 valuek1（1 番目のスーパーにある品物の種類の数、1つ目の品物名と値段、2つ目の品物名と値段,,,：整数と文字列の空白区切り）

k2 name1 value1 name2 value2 . . . namek2 valuek2（2 番目のスーパーにある品物の種類の数、1つ目の品物名と値段、2つ目の品物名と値段,,,：整数と文字列の空白区切り）

.

.

kn name1 value1 name2 value2 . . . namekn valuekn（n 番目のスーパーにある品物の種類の数、1つ目の品物名と値段、2つ目の品物名と値段,,,：整数と文字列の空白区切り）

q　（必要な品物の数：整数）

name1　（1 つ目の必要な品物の名前：文字列）

name2　（2 つ目の必要な品物の名前：文字列）

.

.

nameq　（q つ目の必要な品物の名前：文字列）

m　（道の数：整数）

s1 t1 d1　（1 本目の道の情報：空白区切りの整数）

s2 t2 d2　（2 本目の道の情報：空白区切りの整数）

.

.

sm tm dm　（m 本目の道の情報：空白区切りの整数）

si ti di は si番目のスーパー（または家）と ti番目のスーパー（または家）が双方向に行き来することができ、その距離が diであることを示します。

家から（スーパーを経由して）全てのスーパーへ行くことができるような道が与えられます。

n は 10 以下であり、q は 15 以下とします。
ki は 100 以下であり、
品物名の文字列は 20文字を越ず、品物の値段は 10000 を越えません。
また、道の長さは 1000 を越えません。

n が 0 のとき、入力の終わりとします。

Output

各データセットについて、最小の金額と距離を１つの空白で区切って１行に出力して下さい。ただし、必要な品物を全て集められない場合は "impossible" と出力して下さい。

Sample Input

3
3 apple 100 banana 200 egg 300
3 apple 150 banana 100 cola 200
3 apple 100 banana 150 cola 200
3
apple
banana
cola
5
0 2 4
0 1 3
0 3 3
1 2 3
2 3 5
3
3 apple 100 banana 200 egg 300
3 apple 150 banana 100 cola 200
3 apple 100 banana 150 cola 200
4
apple
banana
cola
jump
5
0 2 4
0 1 3
0 3 3
1 2 3
2 3 5
0

Output for the Sample Input

400 10
impossible

Solution in JAVA (judge verdict: Wrong Answer)

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Scanner;

//Provident Housewife
public class Main{

	int[][] dist, wf;
	int[][] d;
	int n;
	
//	int get(int s, int last){
//		if(memo[s][last]!=1<<29)return memo[s][last];
//		int c = 0;
//		for(int i=0;i<=n;i++)if((s>>i&1)>0)c++;
//		if(c<=1)return memo[s][last] = 0;
//		int min = 1<<29;
//		for(int i=0;i<=n;i++){
//			if((s>>i & 1)==0 || i==last)continue;
//			min = Math.min(min, d[i][last]+get(s-(1<<last), i));
//		}
//		return memo[s][last] = min;
//	}
	
	void run(){
		Scanner sc = new Scanner(System.in);
		while(true){
			n = sc.nextInt();
			if(n==0)break;
			Map<String, Integer> ref = new HashMap<String, Integer>();
			int[][] s = new int[n+1][];
			int[][] p = new int[n+1][1000];
			int[] num = new int[n+1];
			int ID = 0;
			for(int i=1;i<=n;i++){
				num[i] = sc.nextInt();
				s[i] = new int[num[i]];
				Arrays.fill(p[i], 1<<29);
				for(int j=0;j<num[i];j++){
					String item = sc.next();
					int id = -1;
					if(ref.containsKey(item))id = ref.get(item);
					else{
						id = ID;
						ref.put(item, ID++);
					}
					p[i][id] = sc.nextInt();
				}
			}
			int q = sc.nextInt();
			int[] need = new int[q];
			boolean enable = true;
			for(int i=0;i<q;i++){
				String item = sc.next();
				if(!ref.containsKey(item)){
					enable = false;
				}
				if(!enable)continue;
				need[i] = ref.get(item);
			}
			d = new int[n+1][n+1];
			for(int i=0;i<=n;i++)Arrays.fill(d[i], 1<<29);
			for(int i=0;i<=n;i++)d[i][i] = 0;
			int m = sc.nextInt();
			for(int i=0;i<m;i++){
				int a = sc.nextInt();
				int b = sc.nextInt();
				int dist = sc.nextInt();
				d[a][b] = d[b][a] = dist;
			}
			if(!enable){
				System.out.println("impossible");continue;
			}
			int[] has = new int[n+1];
			for(int i=1;i<=n;i++){
				for(int j=0;j<q;j++){
					if(p[i][need[j]]!=1<<29)has[i]|=1<<j;
				}
			}
			//O(QN)
			int[][] dp = new int[q][n+1];
			for(int i=0;i<q;i++)Arrays.fill(dp[i], 1<<29);
			for(int i=1;i<=n;i++)dp[0][i] = Math.min(dp[0][i-1], p[i][need[0]]);
			for(int i=1;i<q;i++){
				for(int j=1;j<=n;j++){
					dp[i][j] = Math.min(dp[i][j-1], dp[i-1][j-1] + p[j][need[i]]);
				}
			}
			int optimal = dp[q-1][n];
//			memo = new int[1<<(n+1)][n+1];
//			for(int i=0;i<1<<(n+1);i++)Arrays.fill(memo[i], 1<<29);
//			int min = 1<<29;
			//O(2^n)
			int[] price = new int[1<<(n+1)];
			int Q = (1<<q)-1;
			for(int i=0;i<1<<(n+1);i++){
				int state = 0;
				for(int j=0;j<=n;j++)if((i>>j&1)>0)state|=has[j];
				if(state!=Q){
					price[i] = 1<<29;
					continue;
				}
				int sum = 0;
				for(int j=0;j<q;j++){
					int mm = 1<<24;
					for(int k=1;k<=n;k++){
						if((i>>k&1)==0)continue;
						mm = Math.min(mm, p[k][need[j]]);
					}
					sum += mm;
				}
				price[i] = sum;
			}
			//O(n^3)
			wf = new int[n+1][n+1];
			for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)wf[i][j] = d[i][j];
			for(int k=0;k<=n;k++){
				for(int i=0;i<=n;i++){
					for(int j=0;j<=n;j++){
						wf[i][j] = Math.min(wf[i][j], wf[i][k]+wf[k][j]);
					}
				}
			}
//			boolean[][] u = new boolean[1<<(n+1)][n+1];
			dist = new int[1<<(n+1)][n+1];
			for(int i=0;i<1<<n+1;i++)Arrays.fill(dist[i], 1<<29);
			dist[1][0] = 0;
//			u[1][0] = true;
			PriorityQueue<int[]> que = new PriorityQueue<int[]>(n, new Comparator<int[]>() {
				public int compare(int[] o1, int[] o2) {
					return dist[o1[0]][o1[1]]+wf[o1[1]][0]-dist[o2[0]][o2[1]]-wf[o2[1]][0];
				}
			});
			que.add(new int[]{1, 0});
			int min = 1<<29;
			while(!que.isEmpty()){
				int[] a = que.poll();
				int state = a[0];
				int last = a[1];
				if(price[state]==optimal){
					min = Math.min(min, dist[state][last]+wf[last][0]);
				}
				for(int i=0;i<=n;i++){
					if(i==last||d[last][i]==1<<29||(state>>i&1)>0)continue;
					int ns = state | 1<<i;
//					if(u[ns][i])continue;
					int v = dist[state][last] + d[last][i];
					if(v<dist[ns][i]){
						dist[ns][i] = v;
						que.add(new int[]{ns, i});
					}
				}
			}
			//O(2^n*n)
//			for(int i=0;i<1<<(n+1);i++){
//				int state = 0;
//				for(int j=1;j<=n;j++){
//					if((i>>j&1)>0)state|=has[j];
//				}
//				if(state!=Q)continue;
//				for(int j=1;j<=n;j++){
//					if((i>>j&1)==0|| (i&1)==0)continue;
//					int x = get(i, j) + wf[j][0];
//					if(x>=1<<24)continue;
//					if(price[i]==optimal)min = Math.min(min, x);
//				}
//			}
			System.out.println(optimal+" "+min);
		}
	}
	
	public static void main(String[] args) {
		new Main().run();
	}
}