P1113杂务
题目描述
John的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务11。John有需要完成的nn个杂务的清单,并且这份清单是有一定顺序的,杂务k(k>1)k(k>1)的准备工作只可能在杂务11至k-1k−1中。
写一个程序从11到nn读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定John的农场有足够多的工人来同时完成任意多项任务。
思路:当看到这种题目上说明有前驱的问题一般用拓扑排序解决。
一般拓扑排序共有四个主要步骤:
1.初始化队列,将入度为 0 的节点放入队列。
2.取出队首,遍历其出边,将能够到达的点入度减一,同时维护答案数组。
3.若在此时一个点的入度变为 0,那么将其加入队列。
4.回到第二步,直到队列为空。
这个题的答案就是在不连通的点和连通块中前驱最多的点当中取max,所以维护一个答案数组即可。
拓扑排序ACcode:
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6+10;
int n,ans;
int a[N];
int f[N];//答案数组
int in[N];//记录入度
queue<int>q;//队列用来存入度为0的点
int h[N],e[N],ne[N],idx;//邻接表存图,数组要开点数*边数大小
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
in[b]++;
}
void topsort()
{
for(int i=1;i<=n;i++)
if(in[i]==0)
{
f[i]=a[i];
q.push(i);
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
in[j]--;
if(in[j]==0)q.push(j);
f[j]=max(f[j],f[u]+a[j]);
}
}
}
int main()
{
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
int x;
cin>>x>>a[x];
int t;
while(cin>>t)
{
if(t==0)break;
add(t,x);
}
}
topsort();
for(int i=1;i<=n;i++)ans=max(ans,f[i]);
cout<<ans<<endl;
return 0;
}
其实这个还可以通过模拟的方法来写,代码量也比较小 巩固好拓扑排序可以用模拟写一下
ACcode
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e4+10;
int n,ans[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int id,len,t;
cin>>id>>len;
int vis=0;//用来标记最大的前驱
while(cin>>t&&t)
{
if(ans[t]>ans[vis])vis=t;
}
ans[id]=len+ans[vis];
}
sort(ans+1,ans+n+1);
cout<<ans[n]<<endl;
return 0;
}