晴神机试模拟题选解 1044 关键路径

晴神机试模拟题选解 1044 关键路径

题目:

给定一个有N个顶点、M条边的有向图,顶点下标为从1到N,每条边都有边权。判断这个有向图是否是有向无环图,如果是的话,请处理K个查询,每个查询为图中的一条边,求这条边的最早发生时间和最晚发生时间。最后再输出图中的所有关键路径。

输入
每个输入文件中一组数据。

第一行为两个整数N、M,表示有向无环图的顶点数和边数(1<=N<=1000, 0<=M<=N*(N-1)),顶点编号为从1到N。

接下来M行,每行为三个正整数u、v、w(1<=u,v<=N,0<w<=20,u!=v),分别表示有向边的起点、终点、边权。数据保证不会有两条起点和终点都相同的边。

然后是一个正整数K(1<=K<=1000),表示查询个数。

接着是K行,每行为两个正整数u、v,分别表示查询边的起点和终点。数据保证查询边一定是图上存在的边。

输出
如果给出的图不是有向无环图,那么在一行里输出NO,后面的查询结果和关键路径均不需要输出;

如果给出的图是有向无环图,那么在一行里输出YES,接着输出下面的内容:

每个查询一行,输出查询边的最早发生时间和最晚发生时间;

之后一行输出一个整数:关键路径上的边权之和;

最后若干行输出所有关键路径,每行表示其中一条,格式为用->连接的顶点编号。注意,如果有两条关键路径a[1]->a[2]->…->a[k]->a[k+1]->…与b[1]->b[2]->…->b[k]->[k+1]->…,满足a[1]==b[1]、a[2]==b[2]、…、a[k]==b[k]、a[k+1]<b[k+1],那么把关键路径a优先输出。数据保证关键路径条数不超过10000条。

Sample Input 1
4 5
1 2 3
1 3 2
1 4 5
2 4 1
3 4 3
2
1 3
2 4
Sample Output 1
YES
0 0
3 4
5
1->3->4
1->4

Sample Input 2
3 3
1 2 3
2 3 1
3 2 2
2
1 2
2 3

Sample Output 2
NO

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1111;
struct node{
	int v,w;
};
vector<node> G[maxn];
vector<int> toporder,pre[maxn],path;
int e[maxn][maxn],l[maxn][maxn];
int n,m,ed,st,maxlen=-1,ind[maxn],ve[maxn],vl[maxn],origin[maxn];
bool istoplogical()
{
	queue<int> q;
	for(int i=1;i<=n;i++) if(ind[i]==0) q.push(i);
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		toporder.push_back(now);
		for(int i=0;i<G[now].size();i++)
		{
			int v=G[now][i].v;
			ind[v]--;
			if(0==ind[v]) q.push(v);
			if(ve[now]+G[now][i].w>ve[v]) ve[v]=ve[now]+G[now][i].w;
		}
	}
	if(toporder.size()==n) return true;
	else return false;
}
void DFS(int u)
{
    if(pre[u].size()==0)
	{
        path.push_back(u);
        for(int i=0;i<path.size();i++)
		{
            cout<<path[i];
            if(i<path.size()-1) cout<<"->";
            else cout<<endl;
        }
        path.pop_back();
    }
    path.push_back(u);
    sort(pre[u].begin(),pre[u].end());
    for(int i=0;i<pre[u].size();i++) DFS(pre[u][i]);
    path.pop_back();
}
int main()
{
	cin>>n>>m;
	int u,v,w,k;
	for(int i=0;i<m;i++)
	{
		cin>>u>>v>>w;
		ind[v]++;
		origin[v]++;
		G[u].push_back(node{v,w});
	}
	if(istoplogical())
	{
    	for(int i=1;i<=n;i++) if(ve[i]>maxlen) maxlen=ve[i];
		fill(vl,vl+maxn,maxlen);
		while(toporder.size()!=0)
		{
			int u=toporder[toporder.size()-1];
			toporder.pop_back();
			for(int i=0;i<G[u].size();i++)
			{
				v=G[u][i].v;
				if(vl[v]-G[u][i].w<vl[u]) vl[u]=vl[v]-G[u][i].w;
			}
		}
		for(int u=1;u<=n;u++)
		{
			for(int i=0;i<G[u].size();i++)
			{
				int v=G[u][i].v,w=G[u][i].w;
				e[u][v]=ve[u];
				l[u][v]=vl[v]-w;
				if(e[u][v]==l[u][v]) pre[u].push_back(v);
			}
		}
		printf("YES\n");
		cin>>k;
		while(k--)
		{
			cin>>u>>v;
			printf("%d %d\n",e[u][v],l[u][v]);
		}
		printf("%d\n",maxlen);
		for(int i=1;i<=n;i++) if(origin[i]==0&&pre[i].size()!=0) DFS(i);
	}
	else printf("NO\n");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值