现在的位置: 首页 > 综合 > 正文

双连通+强连通整理

2018年02月21日 ⁄ 综合 ⁄ 共 3886字 ⁄ 字号 评论关闭

BNU 28903 :Unique Path 双连通分量 + 查找边的数量

传送门

一道很简单的题,在一张无向图上,去掉所有的双连通分量,求剩下的树(除去双连通分量之后剩下的必为树和单独的点)里面的边的数量,我试着用dfs搜,也就是Tarjan的方法去做,但是一开始DFS写挫了,边的访问没有写好导致无法找出分量的位置,来回调整了好久。

这里处理好之后后面就简单了,找出所有的树然后边相加就行了(用并查集实现的)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <list>
#include <map>

using namespace std;

#define N 100005

struct edges
{
    int u,v;
    int next;
} edge[N * 2];
int head[N];
int dfn[N];
bool used[N];
int low[N];
int n,m;
int shu,pre;
int wei[N];
int ans[N];
long long sum;

void add(int u,int v)
{
    edge[shu].u = u;
    edge[shu].v = v;
    edge[shu].next = head[u];
    head[u] = shu;
    shu++;
}

void Init()
{
    memset(edge,0,sizeof(edge));
    memset(used,false,sizeof(used));
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    pre = 1;
    int u,v;
    shu = 0;
    for(int i=0; i<m; i++)
    {
        scanf("%d%d",&u,&v);
        {
            add(u,v);
            add(v,u);
        }
    }
}

void dfs(int u, int t)
{
    dfn[u] = low[u] = pre;
    pre++;
    for(int i = head[u]; i; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            dfs(v,u);
            low[u] = min(low[u] , low[v]);
            if(low[v] <= dfn[u])
                used[u] = used[v] = true;
            continue;
        }
        if(v != t)
            low[u] = min(low[u],dfn[v]);

    }
    //cout<<u<<' '<<dfn[u]<<' '<<low[u]<<'a'<<endl;
}

int find(int x)
{
    if(x == wei[x])
    {
        return x;
    }
    int t = find(wei[x]);
    wei[x] = t;
    return t;
}

int main()
{
    int t,cas = 0;
    scanf("%d",&t);
    while(t--)
    {
        cas++;
        scanf("%d%d",&n,&m);
        Init();
        for(int i=1; i<=n; i++)
        {
            if(!used[i])
            {
                dfs(i,0);
            }
        }
        //cout<<'a'<<endl;
        for(int i=1;i<=n;i++)
        {
            wei[i] = i;
            ans[i] = 1;
        }
        for(int i=1; i<=n; i++)
        {
            for(int j = head[i];j;j = edge[j].next)
            {
                int v = edge[j].v;
                if(!used[i] && !used[v])
                {
                    int x = find(i);
                    int y = find(v);
                    if(x != y)
                    {
                        wei[y] = x;
                        ans[x] += ans[y];
                        ans[y] = 0;
                    }
                }
            }
        }
//        for(int i=1;i<=shu;i++)
//        {
//            cout<<ans[i]<<' ';
//        }
//        cout<<endl;
        sum = 0;
        memset(used,false,sizeof(used));
        for(int i=1;i<=n;i++)
        {
            int x = find(i);
            if(!used[x])
            {
                used[x] = true;
                sum += ans[x] * (ans[x] - 1) / 2;
                //cout<<sum<<' '<<ans[x]<<' '<<x<<endl;
            }
        }
        printf("Case #%d: %lld\n",cas,sum);
    }
    return 0;
}

HDU4635 : Strongly connected 强连通分量+出入度判断

传送门

既然做了双连通,自然想到了去把强连通也写一下(本质上双连通的写法是按强连通来的)

这道题是求在已知的有向图上最多能加多少条边(不能使已有的图整体强连通)如果本身给的就是强连通图,输出-1。

这道题我们需要的就是把所有的强连通分量都求出来(包括点),然后找出其中能添加的边个数。

因为出入度都为1的点在这里我们不能把其当做是一个独立的强连通分量,所以需要判断出入度。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <list>
#include <map>

using namespace std;

#define N 100005

struct Edges
{
    int u,v;
    int next;
} edge[N*2];
int head[N];
int dfn[N];
int low[N];
int fan[N];
bool used[N];
bool use[N];
int n,m,shu,front;
vector<int> Vector;
int num,pre;
int ans[N];
bool in[N];
bool out[N];

void add(int u,int v)
{
    edge[shu].u = u;
    edge[shu].v = v;
    edge[shu].next = head[u];
    head[u] = shu;
    shu++;
}

void Init()
{
    memset(edge,0,sizeof(edge));
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(used,false,sizeof(used));
    memset(use,false,sizeof(use));
    memset(fan,0,sizeof(fan));
    int u,v;
    num = pre = 0;
    shu = 1;
    for(int i=0; i<m; i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
}

void dfs(int u)
{
    //cout<<u<<' '<<edge[head[u]].v<<'b'<<endl;
    front++;
    pre++;
    Vector.push_back(u);
    dfn[u] = low[u] = pre;
    used[u] = use[u] = true;
    for(int i = head[u]; i; i = edge[i].next)
    {
        int v = edge[i].v;
        //cout<<v<<'c'<<endl;
        if(used[v])
        {
            if(use[v]) low[u] = min(low[u],dfn[v]); //必须加入这个来判重和去重,不然分量的计算会出现偏差
            continue;
        }
        dfs(v);
        low[u] = min(low[u],low[v]);
    }
    if(low[u] == dfn[u])
    {
        //cout<<u<<endl;
        num++;
        int mark = Vector.back();
        Vector.pop_back();
        fan[mark] = num;
        use[mark] = false;
        while(mark != u)
        {
            mark = Vector.back();
            Vector.pop_back();
            fan[mark] = num;
            use[mark] = false;
        }
    }
}

int main()
{
    int t,cas = 0;
    scanf("%d",&t);
    while(t--)
    {
        cas++;
        scanf("%d%d",&n,&m);
        Init();
        for(int i=1; i<=n; i++)
        {
            //cout<<i<<' '<<used[i]<<endl;
            if(!used[i])
            {
                front = -1;
                Vector.clear();
                dfs(i);
            }
        }
        //cout<<num<<endl;
        if(num == 1)
        {
            printf("Case %d: -1\n",cas);
            continue;
        }
        memset(ans,0,sizeof(ans));
        memset(in,false,sizeof(in));
        memset(out,false,sizeof(out));
        for(int i=1;i<=n;i++)
        {
            ans[fan[i]]++;
        }
        for(int i=1;i<shu;i++)
        {
            int x = fan[edge[i].u];
            int y = fan[edge[i].v];
            if(x != y)
            {
                in[y] = true;
                out[x] = true;
            }
        }
        int sum = N;
        for(int i=1;i <= num;i++)
        {
            if(!in[i] || !out[i])sum = min(sum,ans[i]);
        }
        //cout<<sum<<endl;
        sum =  n * (n - 1) - sum * (n - sum) - m;
        printf("Case %d: %d\n",cas,sum);
    }
    return 0;
}

抱歉!评论已关闭.