图论专题 - 解题报告 - A

本文探讨如何在有向无环图(DAG)中添加边而不形成环。关键在于,当新边加入时,总能找到一种方向使其保持无环状态。通过拓扑排序判断有向图是否存在环,并利用二分搜索策略找到能保持无环状态的边的最大长度k。提供了解题代码实现。
摘要由CSDN通过智能技术生成

给你一些点,试着连入有向边,使不成环,要怎么做呢?就是在将要成环的时候突然反向,就一定不会成环。问题就从调整边转化为加入新边。试着这样去想,在合法的情况下加入一个变化,会造成什么样的影响呢?
重要结论:如果有在一颗有向边的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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值