51nod-生产口罩(拓补排序+DP)by zyz

题目:生产口罩

链接:http://class.51nod.com/Classes/Problem.html#courseProblemId=1718&classId=129
//注:题目来自51nod
这道题是一道集拓补排序和DP的好题,而且还有几个细节值得注意,先讲大体思路,再讲细节。
题目:
在这套方案里,有n个自动化工厂,分别对应着生产口罩的不同工序。不过,一些自动化工厂要开始进行自己的工序,必须要先等待另外一些工厂完成自己的工序,我们把这些工序称为前置工序。

每个工厂完成自己的工序都需要一定的时间,且只有所有工厂都完成自己的工序,口罩生产才能完成。

现在告诉你每个工厂完成工序需要的时间,以及每个工厂进行自己工序需要的前置工序,问最早什么时候口罩生产才能完成

思路:
实际上所谓的“前置工序”实际上就是有向边,一个工厂是一个节点,整个方案就是一个有向图
所以这不就是套拓补排序模板吗

在此题中我们的目的是求出最短时间,所以对于每一个点,我们都需要求出Ta的最短时间。但怎么求呢?
对于每一个点都有很多个入边,因此我们只需要求出Ta所有入边的最短时间,在选择一个最长的加上本点的时间即可。

代码实现:
我们都知道拓补排序的核心思路是每次删去一条边,然后判断此点是否被“解锁”,是则加入队列,但是如果我们删除了所有通向一个点A的边,那我们怎么才能求出最短时间呢?
我们可以设time[i]表示完成i点的任务需要时间,ans[i]表示从头到完成i点需要的总时间,fa表示通向i节点的节点,在删除之前,比较到底是ans[i](即以前求出的最长路经)大还是time[i]+ans[fa](从fa节点到i节点)大,选择更大的一个赋值给ans[i]即可。

细节方面:
第一个细节:从哪一个节点开始?
如图:
在这里插入图片描述
这种图像该从哪一个开始Bfs?
我目前看到的题解是说在Ta们(入度为零)前面再建立一个节点,并指向Ta们,如图
在这里插入图片描述
但是这样做可能会很麻烦,于是可以这么做,对于入度为零的节点,直接把Ta放在队列里,并以第一个入队的开始,这样就不会忽略一个节点了。

第二个细节:最终答案怎么算?
还是那张图:
在这里插入图片描述
如果按Bfs算的话最终结果一定是6,但是4也得算啊,那怎么办呢?
还是那个题解,Ta的想法是在这种情况下,在像4,6这样的节点后在加一个新节点,如图:
在这里插入图片描述
这就更麻烦了对不对?那怎么办呢?
实际上更简单,因为最短时间只会随着有向边逐渐增加,而不会减小,所以拓补排序结束以后我们就一个一个的遍历节点,找到最大的ans[]即可。

喜闻乐见的代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<string>
#define Max(a,b) (a) > (b) ? (a) : (b)
using namespace std;
int n,m;
int book[200010];
int enter[200001];
int head = 1,tail = 1;
int que[2000001];
long long ans[200001];
long long time[200001];
vector<int> G[200001];
void Bfs(int st){
	while(head != tail){
		int t = que[head];
		for(int i = 0,len = G[t].size() ;i < len;i++){
			enter[G[t][i]]--;
			ans[G[t][i]] = Max(ans[G[t][i]],ans[t] + time[G[t][i]]);
			if(enter[G[t][i]] == 0){
				que[tail++] = G[t][i];
				book[G[t][i]] += 1;
			}
		}
		head++;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	int u,v;
	for(int i = 1;i <= n;i++){
		scanf("%lld",&time[i]);
		ans[i] = time[i];
	}
	for(int i = 1;i <= m;i++){
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		enter[v]++; 
	}
	int t = -1;
	for(int i = 1;i <= n;i++){
		if(enter[i] == 0){
			t = i;
			que[tail++] = i;
		}
	}
	Bfs(t);
	long long res = -99999999;
	for(int i = 1;i <= n;i++){
		res = Max(res,ans[i]);
	}
	printf("%lld",res);
	return 0;
}

本文到这里结束了,感谢阅读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值