CF22E 加最少的边使原有向图边成强连通图

10 篇文章 0 订阅

这道题千万不要一般化:先求强连通分量再把图化为 DAG 来做(我们能够很方便的得到需要添加的边的数量,但是加哪些边会变得很麻烦) 给每个定义一个起点和终点,然后按照下面的做就行了:

            当整个图只有一个环的时候,不可能通过加边使得其成为强连通图!

            链接相邻的两个分块(分块 A 的终点连向分块 B 的起点)

            对于分块中不是起点的入度为 0 的点,建一条反向边

注意一个细节:每个点的出度必为 1,然后,其实都可以化为找分块A终点连向B起点

有什么特点?

    从一个点 u 出发 DFS 遍历所有能够遍历到的点,DFS 结束的时候必定得到一个环!而且,因为每个点的出度为 1,所有遍历到的点只能形成一个环!而且这个环还是在路径的结尾,如果把这个换缩成一个点,那么我们等够得到的是一个“倒着长”的树(只存在从叶子节点到树根的节点,这个环缩成树根了)
#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
const int maxn=100005;
int in[maxn],col[maxn];
vector<int>s[maxn],b,e;
int dfs(int p)//找环
{
    col[p]=1;
    int v=s[p][0];
    if(!col[v])
        return col[p]=dfs(v);
    else return col[p]=p;
}
int main()
{
    int a,n;
    ios::sync_with_stdio(false);
    while(cin>>n)
  // cin>>n;
    {
        memset(in,0,sizeof(in));
        memset(col,0,sizeof(col));
        for(int i=1; i<=n; i++)
        {
            cin>>a;
            in[a]++;
            s[i].push_back(a);
        }
        int k=0;
        for(int i=1; i<=n; i++)
        {
            if(!in[i])
            {
                b.push_back(i);
                e.push_back(dfs(i));
                k++;
            }
        }
        int t=k;
        for(int i=1; i<=n; i++)//这块是说刚开始没找到的,比如1-》2,2—》3,3-》1,1-》4,你从1开始如果先到2,那么4这块不会走,所以要处理一下4的情况
        {
            if(!col[i])
            {
                b.push_back(i);
                e.push_back(dfs(i));
                ++k;
            }
        }
        if(k==1&&t==0)//刚开始谁都没染色,如果成环的话,那么只染色一次,k=1,并且t=0,因为入度没有为0的
        {
            k=0;
        }
        cout<<k<<endl;
        for(int i=0; i<k; i++)
            cout<<e[i]<<" "<<b[(i+1)%k]<<endl;
            for(int i=1;i<=n;i++)
            {
                s[i].clear();
            }
            e.clear();
            b.clear();
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值