1. 程式人生 > >214C 】Game (拓撲排序,思維)

214C 】Game (拓撲排序,思維)

題幹:

Furik and Rubik love playing computer games. Furik has recently found a new game that greatly interested Rubik. The game consists of n parts and to complete each part a player may probably need to complete some other ones. We know that the game can be fully completed, that is, its parts do not form cyclic dependencies.

Rubik has 3 computers, on which he can play this game. All computers are located in different houses. Besides, it has turned out that each part of the game can be completed only on one of these computers. Let's number the computers with integers from 1 to 3. Rubik can perform the following actions:

  • Complete some part of the game on some computer. Rubik spends exactly 1 hour on completing any part on any computer.
  • Move from the 1-st computer to the 2-nd one. Rubik spends exactly 1 hour on that.
  • Move from the 1-st computer to the 3-rd one. Rubik spends exactly 2 hours on that.
  • Move from the 2-nd computer to the 1-st one. Rubik spends exactly 2 hours on that.
  • Move from the 2-nd computer to the 3-rd one. Rubik spends exactly 1 hour on that.
  • Move from the 3-rd computer to the 1-st one. Rubik spends exactly 1 hour on that.
  • Move from the 3-rd computer to the 2-nd one. Rubik spends exactly 2 hours on that.

Help Rubik to find the minimum number of hours he will need to complete all parts of the game. Initially Rubik can be located at the computer he considers necessary.

Input

The first line contains integer n (1 ≤ n ≤ 200) — the number of game parts. The next line contains n integers, the i-th integer — ci (1 ≤ ci ≤ 3) represents the number of the computer, on which you can complete the game part number i.

Next n lines contain descriptions of game parts. The i-th line first contains integer ki (0 ≤ ki ≤ n - 1), then ki distinct integers ai, j (1 ≤ ai, j ≤ nai, j ≠ i) — the numbers of parts to complete before part i.

Numbers on all lines are separated by single spaces. You can assume that the parts of the game are numbered from 1 to n in some way. It is guaranteed that there are no cyclic dependencies between the parts of the game.

Output

On a single line print the answer to the problem.

Examples

Input

1
1
0

Output

1

Input

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

Output

7

Note

Note to the second sample: before the beginning of the game the best strategy is to stand by the third computer. First we complete part 5. Then we go to the 1-st computer and complete parts 3 and 4. Then we go to the 2-nd computer and complete parts 1 and 2. In total we get 1+1+2+1+2, which equals 7 hours.

題目大意:

現在有三個工作站,有三種工作,每種工作需要完成前置任務才能進行當前工作,三個工作站之間轉換需要花費時間,問將所有任務都完成需要花費的最少時間。一開始可以在任意一個工作站開始工作。

解題報告:

   通過他給的那個轉化時間我們就知道這裡面不簡單、、、應該有可以化簡的地方。一看果然,因為啊你會發現按照1->2->3->1的方式迴圈,每次都需要1單位時間,但是如果反過來,任意一個步驟都需要花費2時間,所以我們就想啊,比如2->1花費2單位時間,那麼我們為什麼不2->3->1呢?也是2時間,並且還多操作了一個機器的。。。肯定比之前的方案要優秀啊。所以根據數學歸納法我們發現就按照1->2->3->1->2->3->1這樣的方式迴圈就好了。這樣就把可操作步驟從6個降到了3個,其實也就是一個,一種順序。所以接下來就是選擇開始的機器了。。。沒法證明啊所以我們就暴力好咯。。。

AC程式碼:(62ms)

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
vector<int> vv[505];
int a[505];
int in[505];
int bk[505];
int n;
//int solve(int ss) {
//	int ans=n;
//	int cont=0;
//	int now=ss;
//	while(1) {
//		while(1) {
//			int flag=0;
//			for(int i=1; i<=n; i++) {
//				if(in[i]==0&&a[i]==now) {
//					flag=1;
//					in[i]=-1;
//					cont++;
//					for(int j=0; j<vv[i].size(); j++) {
//						int v=vv[i][j];
//						in[v]--;
//					}
//				}
//			}
//			if(flag==0)break;
//		}
//		if(cont==n)break;
//		now++;
//		ans++;
//		if(now==4)now=1;
//	}
//	return ans;
//}
int solve(int st) {
	queue<int> q;
	int res = n;
	for(int i = 1; i<=n; i++) {
		if(in[i] == 0) q.push(i);
	}
	while(!q.empty()) {
		memset(bk,0,sizeof bk);
		while(!q.empty()) {
			int cur = q.front();q.pop();
			if(bk[cur] == 5) {
				q.push(cur);break;
			}
			bk[cur]++;
			if(a[cur] != st) {
				q.push(cur);continue;
			}
			int up = vv[cur].size();
			for(int i = 0; i<up; i++) {
				int v = vv[cur][i];
				in[v]--;
				if(in[v] == 0) q.push(v);
			}
			
		}		
		st = st==3?1:st+1;
		if(!q.empty()) res++;
	}
	return res;
}
int temp[15000];
int main() {
	scanf("%d",&n);
	memset(in,0,sizeof(in));
	for(int i=1; i<=n; i++) vv[i].clear();
	for(int i=1; i<=n; i++) scanf("%d",&a[i]);
	for(int i=1; i<=n; i++) {
		int ki;
		scanf("%d",&ki);
		for(int j=1; j<=ki; j++) {
			int x;
			scanf("%d",&x);
			vv[x].push_back(i);
			in[i]++;
		}
	}
	for(int i=1; i<=n; i++) {
		temp[i]=in[i];
	}
	int ans=0x3f3f3f3f;
	for(int i=1; i<=n; i++) in[i]=temp[i];
	ans=min(ans,solve(1));
	for(int i=1; i<=n; i++) in[i]=temp[i];
	ans=min(ans,solve(2));
	for(int i=1; i<=n; i++) in[i]=temp[i];
	ans=min(ans,solve(3));
	printf("%d\n",ans);
}

AC程式碼2:(62ms)

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
vector<int> vv[505];
int a[505];
int in[505];
int bk[505];
int n;
int solve(int ss) {
	int ans=n;
	int cont=0;
	int now=ss;
	while(1) {
		while(1) {
			int flag=0;
			for(int i=1; i<=n; i++) {
				if(in[i]==0&&a[i]==now) {
					flag=1;
					in[i]=-1;
					cont++;
					for(int j=0; j<vv[i].size(); j++) {
						int v=vv[i][j];
						in[v]--;
					}
				}
			}
			if(flag==0)break;
		}
		if(cont==n)break;
		now++;
		ans++;
		if(now==4)now=1;
	}
	return ans;
}
//int solve(int st) {
//	queue<int> q;
//	int res = n;
//	for(int i = 1; i<=n; i++) {
//		if(in[i] == 0) q.push(i);
//	}
//	while(!q.empty()) {
//		memset(bk,0,sizeof bk);
//		while(!q.empty()) {
//			int cur = q.front();q.pop();
//			if(bk[cur] == 1) {
//				q.push(cur);break;
//			}
//			bk[cur]++;
//			if(a[cur] != st) {
//				q.push(cur);continue;
//			}
//			int up = vv[cur].size();
//			for(int i = 0; i<up; i++) {
//				int v = vv[cur][i];
//				in[v]--;
//				if(in[v] == 0) q.push(v);
//			}
//			
//		}		
//		st = st==3?1:st+1;
//		if(!q.empty()) res++;
//	}
//	return res;
//}
int temp[15000];
int main() {
	scanf("%d",&n);
	memset(in,0,sizeof(in));
	for(int i=1; i<=n; i++) vv[i].clear();
	for(int i=1; i<=n; i++) scanf("%d",&a[i]);
	for(int i=1; i<=n; i++) {
		int ki;
		scanf("%d",&ki);
		for(int j=1; j<=ki; j++) {
			int x;
			scanf("%d",&x);
			vv[x].push_back(i);
			in[i]++;
		}
	}
	for(int i=1; i<=n; i++) {
		temp[i]=in[i];
	}
	int ans=0x3f3f3f3f;
	for(int i=1; i<=n; i++) in[i]=temp[i];
	ans=min(ans,solve(1));
	for(int i=1; i<=n; i++) in[i]=temp[i];
	ans=min(ans,solve(2));
	for(int i=1; i<=n; i++) in[i]=temp[i];
	ans=min(ans,solve(3));
	printf("%d\n",ans);
}

總結:

  拓撲排序最好還是按照下面這個去寫,因為AC程式碼1那裡我本來是if(bk[cur]==1)break; 的,然後WA了,後來想想發現確實是這樣,並且其實我這樣寫也不太對,應該是if(bk[cur] == n)break;或者==100。因為標準的拓撲排序是隻有一層while的。但是這裡為了還有轉移到別的機器的過程,所以這裡用了兩個while,,,所以一定要保證當前機器的已經全部處理完。所以不能 :只要出現過重複的,就說明篩過一遍了,就break。這樣是不對的啊,因為遍歷到a,,然後a在當前機器但是in[a]!=0,所以bk[a]=1並且將a再次入隊。萬一篩的過程中,將in[a]減成0了,也就是a可以被執行了,但是我們bk[a]==1了所以就break了。。。所以這是不對的。為了確保是 確實沒有一個可以操作的了,應該是入隊n次 則break這樣。、、、但是這裡寫的是入隊5次,,也過了。

首先你要知道啊這題算時間是分成兩部分來算的,這也是簡化問題的一個方式,把要求的答案分成幾份,先把已知答案求出來,然後光去搞未知的就可以了。其實要是不這麼寫的話也不難寫,但是就是思路方面更推薦這一種方法比較優秀。