POJ 3177 Redundant Paths 边双连通分支

题目链接:http://poj.org/problem?id=3177

题目大意是 给一个无向图,求至少要添加多少条边才能使其变为边双连通图

边双连通图简单来说就是联通且没有割边(桥)

图是连通的,并且没有给重边(所以程序中没有处理重边)

思想是缩环,然后统计有多少个叶子节点,答案为(叶子节点数目+1)/ 2

统计叶子的过程是先找出所有的割边

然后逐个遍历点,遍历边,若边是割边则去点所在的scc的度数加一

这样到最后度数为1的scc就是叶子

完全参考自kuangbin模板

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
#include <set>
#include <queue>
#include <vector>
#include <ctime>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;

template<class T>
inline bool read(T &n)
{
    T x = 0, tmp = 1;
    char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}

template <class T>
inline void write(T n)
{
    if(n < 0)
    {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n)
    {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
//-----------------------------------

const int maxn = 5010;
const int maxm = 20010;
struct Edge
{
    int to, next;
    bool cut;
}edge[maxm];

int head[maxn], tot;
int Low[maxn], DFN[maxn], Stack[maxn], Belong[maxn];
int Index, top;
int scc;
int bridge;
bool Instack[maxn];
int num[maxn];

void addedge(int u, int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut = false;
    head[u] = tot++;
    edge[tot].to = u;
    edge[tot].next = head[v];
    edge[tot].cut = false;
    head[v] = tot++;
}

void Tarjan(int u, int pre)
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(v == pre)
            continue;
        if(!DFN[v])
        {
            Tarjan(v, u);
            if(Low[u] > Low[v])
                Low[u] = Low[v];
            if(Low[v] > DFN[u])
            {
                bridge++;
                edge[i].cut = true;
                edge[i^1].cut = true;
            }
        }
        else if(Instack[v] && Low[u] > Low[v])
            Low[u] = Low[v];
    }
    if(Low[u] == DFN[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = scc;
            num[scc]++;
        }
        while(v != u);
    }
}

int du[maxn];

void solve(int N)
{
    memset(DFN, 0, sizeof(DFN));
    memset(Instack, 0, sizeof(Instack));
    memset(num, 0, sizeof(num));
    Index = scc = top = 0;
    for(int i = 1; i <= N; i++)
    {
        if(!DFN[i])
            Tarjan(i, 0);
    }

    for(int i = 1; i <= N; i++)
    {
        for(int j = head[i]; j != -1; j = edge[j].next)
        {
            if(edge[j].cut) //若为桥,则去点所在的scc的度+1
                du[Belong[edge[j].to]]++;
        }
    }

    int leaf = 0;
    for(int i = 1; i <= scc; i++)   //找“叶子” 即度为1的节点
    {
        if(du[i] == 1)
            leaf++;
    }

    printf("%d
", (leaf + 1)/ 2);
}

void init()
{
    tot = 0;
    bridge = 0;
    memset(head, -1, sizeof(head));
}

int main()
{
    //freopen("in.txt", "r", stdin);

    int F, R;
    scanf("%d%d", &F, &R);

    init();
    for(int i = 0; i < R; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
    }

    solve(F);

    return 0;
}
原文地址:https://www.cnblogs.com/dishu/p/4517670.html