题目链接:https://vjudge.net/problem/CodeForces-1100E
转自:https://blog.csdn.net/BUAA_Alchemist/article/details/86484660
题意:一张有向图,让你把其中的一些边反向,使得不存在环,其中反向边边权的最大值尽可能的小。输出反向的边的边权最大值和要反向的边。
思路:输出边权最大值,那么就需要二分。二分时用边权大于x的建图,拓扑排序判断是否有环。如果没有环说明当前x偏小,否则偏大。二分完毕后边权大于x的就是不能改变方向的边,也就是说让剩下的边权小于等于x的一些边反向就可以有答案,但是注意不能全部反向,可能又会凑出一个环。 这时候应该对不能改变方向的边进行拓扑排序,然后剩余的边,如果和顶点拓扑序顺序相同,就保留,否则翻转。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int maxn=1e5+5;
int n,m,u[maxn],v[maxn],c[maxn],l,r,d[maxn],o[maxn],on;
vector <int> g[maxn],ans;
queue <int> q;
bool check(int x)
{
for(int i=1;i<=n;i++)
{
d[i]=0;
g[i].clear();
}
for(int i=1;i<=m;i++)
{
if(c[i]>x)
{
g[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 i=0;i<g[u].size();i++)
{
d[g[u][i]]--;
if(d[g[u][i]]==0)
{
q.push(g[u][i]);
}
}
}
for(int i=1;i<=n;i++)
{
if(d[i]!=0)
return false;
}
return true;
}
int main()
{
l=r=0;
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)
{
int mid=(l+r)/2;
if(check(mid))
{
r=mid;
}
else
l=mid+1;
}
for(int i=1;i<=n;i++)
{
d[i]=0;
g[i].clear();
}
for(int i=1;i<=m;i++)
{
if(c[i]>r)
{
d[v[i]]++;
g[u[i]].push_back(v[i]);
}
}
on=0;
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 i=0;i<g[u].size();i++)
{
d[g[u][i]]--;
if(d[g[u][i]]==0)
{
q.push(g[u][i]);
o[g[u][i]]=++on;
}
}
}
for(int i=1;i<=m;i++)
{
if(c[i]<=r&&o[u[i]]>o[v[i]])
{
ans.push_back(i);
}
}
printf("%d %d\n",r,ans.size());
for(int i=0;i<ans.size();i++)
{
if(i)
printf(" ");
printf("%d",ans[i]);
}
return 0;
}