题干:
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 ≤ n; ai, 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次,,也过了。
首先你要知道啊这题算时间是分成两部分来算的,这也是简化问题的一个方式,把要求的答案分成几份,先把已知答案求出来,然后光去搞未知的就可以了。其实要是不这么写的话也不难写,但是就是思路方面更推荐这一种方法比较优秀。