読者です 読者をやめる 読者になる 読者になる

kengo92iの日記

プログラミングとかやったことの記録を書いていきます。

AOJ-2011 : Gather the Maps!

プログラミングコンテスト 動的計画法

Gather the Maps! | Aizu Online Judge

それぞれが持っている地図の一部を集めて、地図を完成させたい。地図は手渡しで一人に集める方式を取ります。それぞれの空いている日にちが与えられるので、それを元に最短何日で地図を完成させられるか。期間内に集める事が出来ない場合は"-1"を出力する。


誰の地図が集まっているかは、ビットを使って表現します。N人の場合は、Nビット用意して、3番目の人の地図が手に入れば、3番目のビットを立てるという感じです。

求めるのは最短日数なので、誰の手元に集めたかという風にするのではなく、全員に渡したとして計算していきます。1番目と3番目の人が集まった場合は、両方に1番目と3番目の地図を渡します。

dp[N][MAX]; //i番目の人がj日目までに集められる最大値(bitで表現)

のように定義して、それぞれの空いている日にちの配列を使って計算します。
空いている日の場合は、他の空いている日にちの人の地図を全て受け取ります。その他の場合は、自分の地図だけを受け取ります。

また、1≦N≦50まであるため、50桁ビットを用意するとint型の範囲を超えるので、long long型にしています。

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2011

#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#include<cmath>
#include<climits>
#include<ctime>
#include<cstring>
#include<numeric>

#define ALL(v) (v).begin(),(v).end()
#define REP(i,p,n) for(int i=p;i<(int)(n);++i)
#define rep(i,n) REP(i,0,n)
#define dump(a) (cerr << #a << "=" << (a) << endl)
#define DUMP(list) cout << "{ "; for(auto nth : list){ cout << nth << " "; } cout << "}" << endl;

using namespace std;

const int MAX = 31;

typedef long long ll;

int main()
{
	int N,M;
	while( cin >> N && N )
	{
		ll dp[N][MAX]; //i番目の人がj日目までに集められる最大値(bitで表現)
		bool is_ok[N][MAX]; //その人の都合を表す配列
		fill_n((int *)dp, sizeof(dp) / sizeof(int), 0);
		fill_n((bool *)is_ok, sizeof(is_ok) / sizeof(bool), false);

		int f;
		rep(i,N)
		{
			cin >> M;
			rep(m,M){
				cin >> f;
				is_ok[i][f] = true;
			}	
		}
		
		REP( i, 0, N ){
			dp[i][0] |= 1LL<<i;
		}

		const ll COMP = (1LL<<N)-1;
		int ans=-1;
		REP( j, 1, MAX ){
			REP( i, 0, N ){
				if(is_ok[i][j]){
					REP( k, 0, N ){
						if(is_ok[k][j]){
							dp[i][j] |= dp[k][j-1];
						}
					}
				}else{
					dp[i][j] |= dp[i][j-1];
				}
	
				if(dp[i][j] == COMP){ ans=j; goto end; }
			}
		}
end:
		cout << ( (ans==-1) ? -1 : ans ) << endl;  
	}
	return 0;
}