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 C# (judge verdict: Time Limit Exceeded)

using System;
using System.Collections;
using System.Collections.Generic;
 
class TEST{
	static void Main(){
		while(true){
			int n=int.Parse(Console.ReadLine());
			if(n==0)break;
			Sol mySol =new Sol(n);
			mySol.Solve();
		}
	}
}

class Sol{
	public void Solve(){
		WF=new int[N+1][];
		for(int i=0;i<=N;i++){
			WF[i]=new int[N+1];
			for(int j=0;j<=N;j++){
				WF[i][j]=Arr[i][j]!=0?Arr[i][j]:(i==j?0:INF);
			}
		}
		//Warshall-Floyd
		for(int kk=0;kk<=N;kk++){
			for(int ii=0;ii<=N;ii++){
				for(int jj=0;jj<=N;jj++){
					WF[ii][jj]=Math.Min(WF[ii][jj],WF[ii][kk]+WF[kk][jj]);
				}
			}
		}
		//MinCostを求めておく。求まらなければ"impossible";
		DCost=new int[q];
		for(int i=0;i<q;i++){
			DCost[i]=INF;
			for(int j=0;j<=N;j++){
				if(D[j].ContainsKey(Okaimono[i])){
					DCost[i]=Math.Min(D[j][Okaimono[i]],DCost[i]);
				}
			}
			if(DCost[i]==INF){
				Console.WriteLine("impossible");
				return;
			}
		}

		
		//総当たり（買い終わったら直帰）
		LMin=INF+1;
		int[] Order=new int[N];
		for(int i=0;i<N;i++)Order[i]=i+1;
		do{
			LMin=Math.Min(LMin,calCost(Order));
		}while(NextPermutaion(Order));
		
		int TotalCost=0;
		for(int i=0;i<q;i++){
			TotalCost+=DCost[i];
		}
		Console.WriteLine("{0} {1}",TotalCost,LMin);
	}
	
	int calCost(int[] Ord){
		
		int ret=0;
		bool[] chk=new bool[q];
		bool finished=false;
		int prev=0;
		
		for(int i=0;i<N;i++){
			ret+=WF[prev][Ord[i]];
			if(ret+WF[Ord[i]][0]>=LMin)return INF;
			finished=true;
			for(int j=0;j<q;j++){
				if(D[Ord[i]].ContainsKey(Okaimono[j])){
					if(D[Ord[i]][Okaimono[j]]==DCost[j]){
						chk[j]=true;
					}
				}
				finished=finished&chk[j];
			}
			if(finished){
				return ret+WF[Ord[i]][0];
			}
			prev=Ord[i];
		}
		
		return INF;
	}
	
	static public bool NextPermutaion<T>(T[] A)where T:IComparable<T>{
		for(int i=A.Length-2;i>=0;i--){
			if(A[i].CompareTo(A[i+1])<0){
				int trgt=Array.FindLastIndex(A,x=>A[i].CompareTo(x)<0);
				Swap(ref A[i],ref A[trgt]);
				Array.Reverse(A,i+1,A.Length-(i+1));
				return true;
			}
		}
		Array.Reverse(A);
		return false;
	}
	static void Swap<T>(ref T x, ref T y){
		T t=x;x=y;y=t;
	}
	
	int[] DCost;
	int LMin;
	int[][] WF;
	
	int N;
	Dictionary<String,int>[] D;
	List<String> Okaimono;
	int[][] Arr;
	int q;
	int E;
	int INF;
	
	public Sol(int n_){
		INF=(int)1e9;
		N=n_;
		D=new Dictionary<String,int>[N+1];
		D[0]=new Dictionary<String,int>();
		for(int i=1;i<=N;i++){
			var ss=rsa();
			D[i]=new Dictionary<String,int>();
			for(int j=1;j<ss.Length;j+=2){
				D[i].Add(ss[j],int.Parse(ss[j+1]));
			}
		}
		Okaimono=new List<String>();
		q=ri();
		for(int i=0;i<q;i++){
			Okaimono.Add(rs());
		}
		E=ri();
		Arr=new int[N+1][];
		for(int i=0;i<=N;i++)Arr[i]=new int[N+1];
		for(int i=0;i<E;i++){
			var d=ria();
			Arr[d[0]][d[1]]=Arr[d[1]][d[0]]=d[2];
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(){return Console.ReadLine().Split(' ');}
	static int[] ria(){return Array.ConvertAll(Console.ReadLine().Split(' '),e=>int.Parse(e));}
	static long[] rla(){return Array.ConvertAll(Console.ReadLine().Split(' '),e=>long.Parse(e));}
	static double[] rda(){return Array.ConvertAll(Console.ReadLine().Split(' '),e=>double.Parse(e));}
}