zoukankan      html  css  js  c++  java
  • 有向图 加最少的边 成为强连通分量的证明 poj 1236 hdu 2767

    poj 1236:

    题目大意:给出一个有向图, 任务一: 求最少的点,使得从这些点出发可以遍历整张图  任务二: 求最少加多少边 使整个图变成一个强连通分量。

     

    首先任务一很好做, 只要缩点 之后 求 入度为0的点 的个数就好了。   因为 缩点后无环,任何一个 入度不为0的点, 沿着入边 倒着走回去一定可以到达一个入度为0的点。

    任务二:

    首先给出结论: 如果整个图已经是一个强连通分量,那么答案是0.  否则求出 缩点后入度为0的点和出度为0的点的个数a,b, 答案就是 max(a,b).

    今天复习图论,第二次做这题了。 记得当时也不是很明白为什么这样是对的,网络上其他人的blog也都找不到证明。 这次觉得不能就这样放过,于是搜到原题的出处是IOI 1996, google到了 当年的官方题解:  http://wiki.ioinformatics.org/w/images/8/81/Ioi96net_sol.pdf

     

    下面让我用自己的话翻译一下:

    定义图G的一个dominator set(和传统的支配集定义好像不太一样?)为一个点集S1,图G的任意一个点都可以从S1集中的某个点出发到达。

    定义图G的一个codominator set为一个点集S2,图G的任意一个点都可以到达S2集中的某个点。

     

    设最小的dominator set是S1,最小的codominator set是S2。

    根据定义很容易知道这里的S1就是任务一求出的那些点, S2就是把所有边反向之后任务一求出的点。

    所以|S1| = 入度为0的点的个数  |S2| = 出度为0的点的个数

    显然 $所需要的加的边数 >= max(|S1|, |S2|)$  因为一个强联通分量没有入度或者出度为0的点。

    下面证明所需要的加的最少的边数是 $max(|S1|,|S2|)$ :

    不妨假设$ |S1| <= |S2| $

    1.如果$|S1| = 1 $ 设 $S1 = {p}   S2 = {q_1, q_2, q_3... q_k}$

    那么连边$<q_1, p>,<q_2, p>,<q_3, p>...<q_k, p>$ 即可以让图变成一个强连通分量。

    因为任意两点$u$, $v$,  存在路径$u o q_i o p o v$。

    2.如果$|S1|>1$  那么 存在 $p_1 p_2 in S1 q_1 q_2 in S2$  根据S1,S2的定义 $p_1$到$q_1$有路, $p_2$到$q_2$有路。 

    那么先连边$<q_1, p_2>$构成一个新图G'。 

    可以证明 $S1'=S1-{p_2}$  $S2'=S2-{q_1}$ 分别是新图的最小dominator set和codominator set。

    先证明S1'和S2'分别是新图的dominator set和codominator set。

    因为原图G中任意$p_2$可以到达的点$v$, 在新图G'中可以从S1'中的点出发,由$p_1 o q_1 o p_2 o v$到达。

    原图G中任意可以到达$q_1$的点$v$, 在新图G'中可以由$v o q_1 o p_2 o q_2$到达S2'中的点。

    在证明它们是最小的(官方题解中漏了这部分,我自己脑补的):

    假设新图G’中存在比S1'还要小的dominator set S,  那么 $S + {p_2}$ 是原图G的dominator set,且比S1小, 这违反了S1是原图G最小的dominator set。

    S2'的证明同理。

    poj 1236代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <cmath>
    #include <map>
    using namespace std;
    
    #define X first
    #define Y second
    #define N 20010
    #define M 350
    
    typedef long long ll;
    const ll INF = 1ll<<60;
    const int Mod = 1000000007;
    
    int dfs_clock, scc_cnt;
    int dfn[N], low[N], scc[N];
    int in[N], out[N];
    int stack[N], top;
    vector<int> E[N];
    
    void Dfs(int x)
    {
        dfn[x] = low[x] = ++dfs_clock; stack[++top] = x;
        for (int i = 0; i < E[x].size(); ++i)
        {
            int y = E[x][i];
            if (!dfn[y]) Dfs(y), low[x] = min(low[x], low[y]);
            else if (!scc[y]) low[x] = min(low[x], dfn[y]);
        }
        
        if (low[x] == dfn[x])
        {
             scc_cnt++;
             int cur;
             do
             {
                 cur = stack[top--];
                 scc[cur] = scc_cnt;
             }while (cur != x);
        }
    }
    
    int main()
    {
        //freopen("A.in","r",stdin);
        //freopen("A.out","w",stdout);
        
        int n; 
        while (scanf("%d", &n) != EOF)
        {
            for (int i = 1; i <= n; ++i) dfn[i] = low[i] = scc[i] = 0, E[i].clear();
            for (int i = 1, x; i <= n; ++i) while (scanf("%d", &x) && x) E[i].push_back(x);
            dfs_clock = scc_cnt = 0;
            for (int i = 1; i <= n; ++i) if (!dfn[i]) Dfs(i);
            for (int i = 1; i <= scc_cnt; ++i) in[i] = out[i] = 0;
            for (int i = 1; i <= n; ++i)
            {
                for (int j = 0; j < E[i].size(); ++j)
                {
                    int x = scc[i], y = scc[E[i][j]];
                    if (x != y) in[y]++, out[x]++;
                }
            }
            int a = 0, b = 0;
            for (int i = 1; i<= scc_cnt; ++i) 
            {
                if (!in[i]) a++;
                if (!out[i]) b++;
            }
            printf("%d
    %d
    ", a, scc_cnt > 1? max(a,b) : 0);
        }
    
        return 0;
    }
    View Code

    双倍经验 hdu 2767代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <cmath>
    #include <map>
    using namespace std;
    
    #define X first
    #define Y second
    #define N 20010
    #define M 350
    
    typedef long long ll;
    const ll INF = 1ll<<60;
    const int Mod = 1000000007;
    
    int dfs_clock, scc_cnt;
    int dfn[N], low[N], scc[N];
    int in[N], out[N];
    int stack[N], top;
    vector<int> E[N];
    
    void Dfs(int x)
    {
        dfn[x] = low[x] = ++dfs_clock; stack[++top] = x;
        for (int i = 0; i < E[x].size(); ++i)
        {
            int y = E[x][i];
            if (!dfn[y]) Dfs(y), low[x] = min(low[x], low[y]);
            else if (!scc[y]) low[x] = min(low[x], dfn[y]);
        }
        
        if (low[x] == dfn[x])
        {
             scc_cnt++;
             int cur;
             do
             {
                 cur = stack[top--];
                 scc[cur] = scc_cnt;
             }while (cur != x);
        }
    }
    
    int main()
    {
        //freopen("A.in","r",stdin);
        //freopen("A.out","w",stdout);
        
        int T, n, m;  scanf("%d", &T);
        while (T-- && scanf("%d %d", &n, &m))
        {
            for (int i = 1; i <= n; ++i) dfn[i] = low[i] = scc[i] = 0, E[i].clear();
            while (m--)
            {
                int x, y;
                scanf("%d %d", &x, &y);
                E[x].push_back(y);
            }
            dfs_clock = scc_cnt = 0;
            for (int i = 1; i <= n; ++i) if (!dfn[i]) Dfs(i);
            for (int i = 1; i <= scc_cnt; ++i) in[i] = out[i] = 0;
            for (int i = 1; i <= n; ++i)
            {
                for (int j = 0; j < E[i].size(); ++j)
                {
                    int x = scc[i], y = scc[E[i][j]];
                    if (x != y) in[y]++, out[x]++;
                }
            }
            int a = 0, b = 0;
            for (int i = 1; i<= scc_cnt; ++i) 
            {
                if (!in[i]) a++;
                if (!out[i]) b++;
            }
            printf("%d
    ", scc_cnt > 1? max(a,b) : 0);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    多态及鸭子类型
    面向对象三大特性之——继承
    类的组合
    类的成员和命名空间
    JAVA中常用的类
    JAVA自学笔记(5)
    JAVA自学笔记(4)
    JAVA自学笔记(3)
    JAVA自学笔记(2)
    JAVA自学笔记(1)
  • 原文地址:https://www.cnblogs.com/vb4896/p/6744584.html
Copyright © 2011-2022 走看看