[ANOJ]1020万妖瞬击[关键路径-最长路]

1. 原题: https://logn.me/problem/1020

2. 思路:

题意:关键路径题。
思路:
显然这题可以采用经典的关键路径算法。
我觉得麻烦,类比dijkstra求最短路,用的递归求最长路。
注意,存在多个源点,所以这里添加一个
超级总源点N。
对于存在环的情况,还是用了拓扑排序判断的。
已AC.

3. 源码:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

const int Max = 1002;
int N, M;
int G[Max][Max] = { 0 }; //**存储DAG(有向无环图)
int dg[Max] = { 0 };//**记录点的入度
int ds[Max] = { 0 };//**记录某点出发的最长路,即关键路径长度
int cnt = 0;
vector<int> pt[Max], tp;//某点的后继,存储的打印路径

bool toporder(); //经典的拓扑排序
int dfs(int x);//递归获取最长路
void dfs_pt(int x);//递归打印路径

int main()
{
	//freopen("in.txt", "r", stdin);
	scanf("%d %d", &N, &M);
	int ca, cb, ct;
	for (int i = 0; i < M; i++)
	{
		scanf("%d %d %d", &ca, &cb, &ct);
		G[ca][cb] = ct;
		dg[cb]++; //记录入度
	}
	if (!toporder()) //存在环
		printf("NO %d\n", N - cnt);
	else
	{
		dfs(N);
		printf("YES %d\n", ds[N]-1);
		dfs_pt(N);
	}

	return 0;
}

bool toporder()
{
	queue<int> Q;
	
	for (int i = 0; i < N; i++)
	{
		if (dg[i] == 0)
		{
			Q.push(i);
			G[N][i] = 1; //在这里,把N记为总源点,到初始源点长度1.
		}
	}
	while (!Q.empty())
	{
		int ft = Q.front();
		Q.pop();
		cnt++;
		for (int i = 0; i < N; i++)
		{
			if (G[ft][i] > 0)
			{
				if (--dg[i] == 0)
					Q.push(i);
			}
		}
	}

	if (cnt == N)
		return true;
	else
		return false;
}
int dfs(int x)
{
	if (ds[x] > 0) //**递归边界,注意不是0
		return ds[x];

	for (int i = 0; i < N; i++)
	{
		if (G[x][i] > 0)
		{
			int tmp = dfs(i) + G[x][i];
			if (tmp > ds[x]) //更新最长距离
			{
				ds[x] = tmp;
				pt[x].clear();
				pt[x].push_back(i);
			}
			else if (tmp == ds[x])
			{
				pt[x].push_back(i); //存在多个后继点
			}
		}
	}

	return ds[x];
}
void dfs_pt(int x) //**递归打印
{
	if (pt[x].size() < 1) //**边界
	{
		for (int i = 0; i < tp.size(); i++)
		{	//**tp数组里没有保存总源点
			if (i != 0)
				printf("->");
			printf("%d", tp[i]);
		}
		printf("\n");
		return;
	}

	for (int i = 0; i < pt[x].size(); i++)
	{
		tp.push_back(pt[x][i]);
		dfs_pt(pt[x][i]);
		tp.pop_back();
	}

	return;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值