Problem:
Problem F: Gather the Maps!

はるか昔、八尾氏が残したとされる伝説の秘宝が八王子のどこかに眠っているという。 その在処を示すとされる宝の地図は、いくつかの断片に分割された状態で、八尾氏の n 人の子孫達によって受け継がれている。

今、八尾氏の子孫達は協力してその秘宝を手に入れようとしていた。 ところが、秘宝の在処を指し示す宝の地図の一部分だけでは秘宝を見つけることができない。 そこで、八尾氏の子孫達は全員で集まって地図を 1 ヶ所に集めようとした。
ところが、いざ実行に移そうとしてもなかなか予定が合わずに集まることができない。 しかしこの秘宝に関する情報は、一族において秘密裏に伝えられてきた貴重な情報である。 漏洩の危険性を考慮すると、公共の通信手段を用いて地図をやりとりすることなど問題外である。

そこで、子孫同士が直接会って地図を手渡すということを繰り返すことで、ある 1 人の子孫のところに地図を集めることにした。
なお、1 人が 1 日に会える人数に制限はないが、互いにスケジュールが空いていることが必要である。

あなたの仕事は、それぞれの子孫に対するスケジュールの空いている日のリストから、地図を集めるには最低で何日必要かを求めるプログラムを書くことである。

ちなみに、八尾氏一族の結束は非常に固い。 最終的に地図全体を手にした子孫が、他の子孫を裏切って秘宝を持ち逃げすれば、一族から制裁を受けることになる。その制裁はきわめて恐ろしいものであるため、実際にその子孫が秘宝を持ち逃げすることは事実上不可能である。

Input

入力は複数のデータセットからなる。

それぞれのデータセットは複数の行からなる。 その最初の行には、地図の断片を持った者の人数を表す整数 n (1 < n <= 50) が記述されている。 続く n 行には、それぞれの子孫のスケジュールが書かれている。
i 行目は i 人目の子孫のスケジュールが表しており、いくつかの整数が 1 文字のスペースを区切りとして書かれている。
最初の整数 fi (0 <= fi <= 30) は、その子孫のスケジュールが空いている日の日数を表す整数である。 続く fi 個の整数は、スケジュールが空いている日付を表す。 これらの日付は互いに異なり、全て 1 以上 30 以下である。

入力の最後に 0 のみを含んだ 1 行がある。

Output

各データセットに対して、1 つの整数を 1 行に出力せよ。
もし、30 日以内に地図を集めることができる場合は、地図を集めるのに最低限必要となる日数を、集めることができない場合は -1 を出力せよ。

追記 :
上記の「地図を集めるのに最低限必要となる日数」は 1 日を起点として最も早く全ての地図が集まる日付を意味する．

Sample Input

4
1 1
2 2 3
2 1 2
3 3 4 5
0

Output for the Sample Input

3

Solution in JAVA (judge verdict: Memory Limit Exceeded)

import java.util.*;
import static java.lang.Math.*;
public class Main {
	public static void main(String[] args) {
		new Main().init();
	}
	void init(){
		new AOJ2011();
	}
}
class AOJ2011{
	final Scanner sc = new Scanner(System.in);
	AOJ2011(){
		while(sc.hasNext()){
			n=sc.nextInt();
			if(n==0)	break;
			solve();
		}
	}
	int n;
	ArrayList<ArrayList<Integer>> f;
	void solve(){
		f=new ArrayList<ArrayList<Integer>>(n);
		for(int i=0; i<=30; ++i)f.add(new ArrayList<Integer>());
		for(int i=0; i<n; ++i){
			int m=sc.nextInt();
			for(int j=0; j<m; ++j){
				int d=sc.nextInt();
				f.get(d).add(i);
			}
		}
		
//		System.out.println(f);
		
		int l=-1,r=31,c=0;
		while(l+1<r){
			c=(l+r)/2;
			int[] init=new int[n];
			for(int j=0; j<n; ++j)init[j]=1;
			if( dfs(init, 1, c) ){
				r=c;
			}else{
				l=c;
			}
		}
		System.out.println(r>30? -1: r);
		
//		for(int i=1; i<=30; ++i){
//			int[] init=new int[n];
//			for(int j=0; j<n; ++j)init[j]=1;
//			if( dfs(init,1,i) ){
//				System.out.println(i);
//				return;
//			}
//		}
//		System.out.println(-1);
	}
	boolean dfs(int[] list,int depth,int lim){
		
//		System.out.println(depth+" "+Arrays.toString(list));
		
		for(int i=0; i<n; ++i)if(list[i]>=n)	return true;
		
		if(depth>lim)	return false;
		
		ArrayList<Integer> cand=f.get(depth);
		if(cand.size()>=2){
			for(int i=0; i<cand.size(); ++i){
				int[] prev=new int[cand.size()];
				prev[0]=list[cand.get(i)];
				int k=1;
				for(int j=0; j<cand.size(); ++j){
					if(i==j)	continue;
					prev[k++]=list[cand.get(j)];
					list[cand.get(i)]+=list[cand.get(j)];
					list[cand.get(j)]=0;
				}
				if( dfs(list, depth+1, lim) )	return true;
				list[cand.get(i)]=prev[0];
				k=1;
				for(int j=0; j<cand.size(); ++j){
					if(i==j)	continue;
					list[cand.get(j)]=prev[k++];
				}
			}
		}else{
			if( dfs(list, depth+1, lim) )	return true;
		}
		
		return false;
		
	}
}

class AOJ2005{
	final Scanner sc = new Scanner(System.in);
	AOJ2005(){
		while(sc.hasNext()){
			int n=sc.nextInt(),m=sc.nextInt(),s=sc.nextInt(),g1=sc.nextInt(),g2=sc.nextInt();
			if(n==0)	break;
			--s; --g1; --g2;
			solve(n,m,s,g1,g2);
		}
	}
	final int INF=Integer.MAX_VALUE/4;
	void solve(int n,int m,int s,int g1,int g2){
		int[][] g=new int[n][n];
		for(int i=0; i<n; ++i)for(int j=0; j<n; ++j)	g[i][j]=INF;
		for(int i=0; i<m; ++i){
			int a=sc.nextInt(),b=sc.nextInt(),c=sc.nextInt();
			--a; --b;
			g[a][b]=c;
		}
		
		ArrayList<HashSet<String>> c1=new ArrayList<HashSet<String>>(n);
		for(int i=0; i<n; ++i)	c1.add(new HashSet<String>());
		
		LinkedList<S1> o1=new LinkedList<S1>();
		o1.add(new S1(s,new String()));
		
		while(!o1.isEmpty()){
			S1 now=o1.poll();
			for(int i=0; i<n; ++i){
				if(now.p==i || g[now.p][i]>=INF)	continue;
				String tmp=now.s+i;
				if(!c1.get(i).contains(tmp)){
					if(i!=g1)	o1.add(new S1(i,tmp));
					c1.get(i).add(tmp);
				}
			}
		}
		
		
		
		HashSet<String> cand=new HashSet<String>();
		cand.addAll(c1.get(g1));
		c1=null;
		System.gc();
		
		int ans=INF;
		for(String str:cand){
			
			int[][] gg=new int[n][n];
			for(int i=0; i<n; ++i)for(int j=0; j<n; ++j)gg[i][j]=g[i][j];
			
			int prev=s,cost1=0;
			for(int i=0; i<str.length(); ++i){
				cost1+=gg[prev][str.charAt(i)-'0'];
				gg[prev][str.charAt(i)-'0']=0;
				prev=str.charAt(i)-'0';
			}
			
			PriorityQueue<S2> o2=new PriorityQueue<S2>();
			o2.add(new S2(s,0));
			int[] c2=new int[n];
			for(int i=0; i<n; ++i)c2[i]=INF;
			
			int cost2=0;
			while(!o2.isEmpty()){
				S2 now=o2.poll();
				if(now.p==g2){
//					System.out.println("G2");
					cost2=now.c;
					break;
				}
				for(int i=0; i<n; ++i){
					if(now.p==i || gg[now.p][i]>=INF)	continue;
					int nc=now.c+gg[now.p][i];
					if(c2[i]<=nc)	continue;
					o2.add(new S2(i, nc));
					c2[i]=nc;
					
				}
			}
			
//			System.out.println(cost1+" "+cost2);
			ans=min(ans,cost1+cost2);
			
		}
		
		System.out.println(ans);
		
	}
	class E{
		int d,c;
		E(int d,int c){
			this.d=d;
			this.c=c;
		}
	}
	class S1{
		int p;
		String s;
		S1(int p,String s){
			this.p=p;
			this.s=s;
		}
	}
	class S2 implements Comparable<S2>{
		int p,c;
		S2(int p,int c){
			this.p=p;
			this.c=c;
		}
		@Override public int compareTo(S2 o){
			return this.c-o.c;
		}
	}
}