【拓扑排序】codeforces1100E Andrew and Taxi

E. Andrew and Taxi
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Andrew prefers taxi to other means of transport, but recently most taxi drivers have been acting inappropriately. In order to earn more money, taxi drivers started to drive in circles. Roads in Andrew’s city are one-way, and people are not necessary able to travel from one part to another, but it pales in comparison to insidious taxi drivers.

The mayor of the city decided to change the direction of certain roads so that the taxi drivers wouldn’t be able to increase the cost of the trip endlessly. More formally, if the taxi driver is on a certain crossroads, they wouldn’t be able to reach it again if he performs a nonzero trip.

Traffic controllers are needed in order to change the direction the road goes. For every road it is known how many traffic controllers are needed to change the direction of the road to the opposite one. It is allowed to change the directions of roads one by one, meaning that each traffic controller can participate in reversing two or more roads.

You need to calculate the minimum number of traffic controllers that you need to hire to perform the task and the list of the roads that need to be reversed.

Input
The first line contains two integers n and m (2≤n≤100000, 1≤m≤100000) — the number of crossroads and the number of roads in the city, respectively.

Each of the following m lines contain three integers ui, vi and ci (1≤ui,vi≤n, 1≤ci≤109, ui≠vi) — the crossroads the road starts at, the crossroads the road ends at and the number of traffic controllers required to reverse this road.

Output
In the first line output two integers the minimal amount of traffic controllers required to complete the task and amount of roads k which should be reversed. k should not be minimized.

In the next line output k integers separated by spaces — numbers of roads, the directions of which should be reversed. The roads are numerated from 1 in the order they are written in the input. If there are many solutions, print any of them.

Examples
inputCopy
5 6
2 1 1
5 2 6
2 3 2
3 4 3
4 5 5
1 5 4
outputCopy
2 2
1 3
inputCopy
5 7
2 1 5
3 2 3
1 3 3
2 4 1
4 3 5
5 4 1
1 5 3
outputCopy
3 3
3 4 7
Note
There are two simple cycles in the first example: 1→5→2→1 and 2→3→4→5→2. One traffic controller can only reverse the road 2→1 and he can’t destroy the second cycle by himself. Two traffic controllers can reverse roads 2→1 and 2→3 which would satisfy the condition.

In the second example one traffic controller can’t destroy the cycle 1→3→2→1. With the help of three controllers we can, for example, reverse roads 1→3 ,2→4, 1→5.

 这个题让部分边反向,使得有向图无环,同时反向的边权的最大值最小。很容易能想到二分答案。每次猜测边权最大值x,然后把不超过x的所有边删除,然后判断剩下的边是否能产生环,能产生环说明x需要增大。否则当前x下存在方案使得部分边反向让原图无环。
 但是构造这个方案却似乎并不容易,因为不会是让所有的边都反向就一定OK的,还要防止反转的边形成新的环。也就是需要挑选部分剩余边反转,既能消除原来的环,又避免产生新环。我尝试了几种DFS方法都WA了,该不会前面二分的check用删除的方式还不够充分吧?!后来看大佬提示,说是用拓扑排序,才茅塞顿开。
 把不可翻转的边构造图,进行拓扑排序,然后剩余的边,如果和顶点拓扑序顺序相同,就保留,否则翻转。简直是很容易理解而又浑然天成的方法呀!看来我只是学习了拓扑排序的一点皮毛,原来关键时候有这种功效!

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#define Mr (L+R>>1)
using namespace std;

int n,m,u[100005],v[100005],c[100005],R,L,d[100005],o[100005],on;
vector<int> E[100005],ans;
queue<int> Q;

bool check(int x)
{
	for(int i=1;i<=n;i++)
		d[i]=0,E[i].clear();
	for(int i=1;i<=m;i++)
		if(c[i]>x)
			E[u[i]].push_back(v[i]),d[v[i]]++;
	for(int i=1;i<=n;i++)
		if(d[i]==0)
			Q.push(i);
	while(!Q.empty())
	{
		int u=Q.front();
		Q.pop();
		for(int j:E[u])
			if(--d[j]==0)	
				Q.push(j);
	}
	for(int i=1;i<=n;i++)
		if(d[i]>0)
			return false;
	return true;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&u[i],&v[i],&c[i]),R=max(R,c[i]);
	while(L<R)
		if(check(Mr))  //二分答案
			R=Mr;
		else
			L=Mr+1;
	for(int i=1;i<=n;i++)
		E[i].clear(),d[i]=0;
	for(int i=1;i<=m;i++)
		if(c[i]>L)
			E[u[i]].push_back(v[i]),d[v[i]]++;
	for(int i=1;i<=n;i++)
		if(d[i]==0)
			Q.push(i),o[i]=++on;
	while(!Q.empty())
	{
		int u=Q.front();
		Q.pop();
		for(int j:E[u])
			if(--d[j]==0)
				Q.push(j),o[j]=++on;
	}
	for(int i=1;i<=m;i++)
		if(c[i]<=L&&o[u[i]]>o[v[i]]) //判断边是否和拓扑序同向
			ans.push_back(i);
	printf("%d %d\n",L,ans.size());
	for(auto i:ans)
		printf("%d ",i);
	return 0;
}
    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/BUAA_Alchemist/article/details/86484660
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞