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

[HDU 2767]Proving Equivalences[Tarjan强连通缩点缩点]

2017年12月13日 ⁄ 综合 ⁄ 共 1562字 ⁄ 字号 评论关闭

题意:

求对一个图补充最少的边使得其成为强连通图.

思路:

缩点是想到了,但是一是对"缩点"认识不够清晰,统计入度出度的时候想着想着又想成了实际的"点",于是就纠结在"不连通的强连通分量没有入度或出度为零的点",然后甚觉错误...

其实啊,"shrink"的时候判断的是一条边两边的id是否相同,这已经是将强连通分量视为一个点了...

一句话, 统计缩点之后的图中入度为零的点的个数, 出度为零的点的个数, 取其大即为答案. 因为从叶子连到根的有向边的条数不会超过这个数, 如果图是不连通的, 将每一个子图先处理成一叶一根(可保持收支平衡),然后首尾相接,仍然是不超过最大叶/根数.

#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 20005;
const int MAXE = 50005;
struct pool
{
    int v,pre;
}p[MAXE];
int num,head[MAXN];
int dfn[MAXN],low[MAXN],id[MAXN],in[MAXN],out[MAXN];
bool vis[MAXN];
int size,Index,n,m;
stack<int> s;

void clear()
{
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(dfn,0,sizeof(dfn));
    memset(head,0,sizeof(head));
    memset(p,0,sizeof(p));
    memset(vis,false,sizeof(vis));
    size = Index = num = 0;
    while(!s.empty()) s.pop();
}

void add(int u, int v)
{
    p[++num].v = v;
    p[num].pre = head[u];
    head[u] = num;
}

void Tarjan(int u)
{
    low[u] = dfn[u] = ++Index;
    vis[u] = true;
    s.push(u);
    for(int tmp = head[u],k;k = p[tmp].v,tmp;tmp = p[tmp].pre)
    {
        if(!dfn[k])
        {
            Tarjan(k);
            low[u] = min(low[u],low[k]);
        }
        else if(vis[k])
            low[u] = min(low[u],low[k]);
    }
    if(dfn[u]==low[u])
    {
        size++;
        int k;
        do
        {
            k = s.top();s.pop();
            vis[k] = false;
            id[k] = size;
        }while(k!=u);
    }
}

void shrink()
{
    for(int i=1;i<=n;i++)
        for(int tmp = head[i],k;k = p[tmp].v,tmp;tmp = p[tmp].pre)
            if(id[i]!=id[k])
            {
                in[id[k] ]++;
                out[id[i] ]++;
            }
}//对"缩点"认识不够彻底啊...

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        clear();//唉唉唉...
        scanf("%d %d",&n,&m);
        for(int i=0,u,v;i<m;i++)
        {
            scanf("%d %d",&u,&v);
            add(u, v);
        }
        for(int i=1;i<=n;i++)
        {
            if(!dfn[i])
            {
                Tarjan(i);
            }
        }
        shrink();
        int in0 = 0, out0 = 0;
        for(int i=1;i<=size;i++)
        {
            if(!in[i])  in0++;
            if(!out[i]) out0++;
        }
        printf("%d\n",(size==1)?0:max(in0,out0));
    }
}

抱歉!评论已关闭.