给你一些点,试着连入有向边,使不成环,要怎么做呢?就是在将要成环的时候突然反向,就一定不会成环。问题就从调整边转化为加入新边。试着这样去想,在合法的情况下加入一个变化,会造成什么样的影响呢?
重要结论:如果有在一颗有向边的DAG上(无环),连上一条新边,一定存在一个方向使得这个新图依旧无环。(这个自己画一下)
我们现在可以反转边的长度在k以下的所有边,那么意思是说,如果比k大的所有有向边连接成的图无环,那么连上调整方向后的所有边,一定会没有环。我们要找的,就是k的最小值。判断有向图是否成环就是拓扑排序就完事了
因为k的大小对于结果图的影响具有单调性,意思是如果调整的最大长度为k时结果图中确定无环,那么调整最大长度为(k + n)的边,结果图确定无环。同样,如果k时已经不能保证调整后结果图无环,那么调整的范围仅仅在(k - n)内的话同样无法保证。所以就有了二分答案的条件。
所以就是拓扑排序 + 二分答案就完事了。带注释代码如下:
#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 200005
#define maxm 55
#define hrdg 1000000007
#define zh 16711680
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;
const double eps = 1e-7;
ll n, m, u, v, d;
struct E {ll u, v, d;} e[maxn<<1]; //保存初始边
bool cmp(E x, E y) {return x.d > y.d;} //初始边排序规则
struct Edge {ll to, nex, dis;} edge[maxn<<1];
ll head[maxn<<1], tot;
void add_edge(ll u, ll v, ll d) {edge[++tot] = {v, head[u], d}; head[u] = tot;} //前向星建图
ll in[maxn], num, cnt;
bool vis[maxn];
inline ll read(){
char c=getchar();long long x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
void init()
{
tot = 0, num = 0, cnt = 0;
memset(head, 0, sizeof(head));
memset(in, 0, sizeof(in));
memset(edge, 0, sizeof(edge));
memset(vis, 0, sizeof(vis));
}
bool tp_judge(ll judge) //判断函数
{
init(); //每次判断前要初始化
queue<ll> q;
for (int i = 1; i <= m; i++)
{
if (e[i].d <= judge) break;
add_edge(e[i].u, e[i].v, e[i].d);
in[e[i].v] ++; //记录入度
vis[e[i].v] = vis[e[i].u] = 1; //标记数据,防止伤害友军
}
for (int i = 1; i <= n; i++)
{
if (!vis[i]) continue;
num++;
if (!in[i])
q.push(i); //如果入度为0,推入队列
}
while (!q.empty())
{
ll now = q.front(); q.pop();
cnt++; //判断是否有环的思想就是如果队列内全部的元素跟出现过的全部元素数量不等,那就判false
for (int i = head[now]; i; i = edge[i].nex)
{
ll to = edge[i].to;
in[to]--;
if (!in[to])
q.push(to);
}
}
return cnt == num;
}
int main()
{
n = read(); m = read();
for (int i = 1; i <= m; i++)
{
u = read(); v = read(); d = read();
e[i] = {u, v, d}; //读入初始边,准备排序后二分
}
sort (e + 1, e + 1 + m, cmp); //排序,从低到高,建立有向图中有超过枚举的长度就break
ll l = 0, r = e[1].d;
while (l < r) //二分答案
{
ll mid = (l + r) >> 1;
if (!tp_judge(mid)) //判断
l = mid + 1;
else
r = mid;
}
printf("%lld\n", l);
return 0;
}