tarjan算法

参考资料:http://www.byvoid.com/blog/scc-tarjan/

几道简单的练习题(hdoj):

强连通:
1269   迷宫城堡                        判断是否是一个强连通
2767 Proving Equivalences      至少加几条边让整个图变成强连通
3836  Equivalent Sets               至少加几条边让整个图变成强连通
1827    Summer Holiday           传递的最小费用
3072    Intelligence System      传递的最小费用
3861 The King’s Problem         强连通+二分匹配
3639 Hawk-and-Chicken          强连通缩点 + 树形dp(累加子节点的总权值)
3594  Cactus                            仙人掌图

 

我的代码:

HDOJ1269 迷宫城堡(风格基本参照byvoid资料)

(模板测试题)

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;

const int NN=10005;

int n,m;
int Dindex,Stop,Bcnt;
bool instack[NN];
int dfn[NN],low[NN];
int Stap[NN];
vector<int> map[NN];

void tarjan(int u)
{   
    int v;
    dfn[u]=low[u]=++Dindex;
    instack[u]=true;
    Stap[++Stop]=u;
    for (int i=0; i<map[u].size(); i++)
    {   
        v=map[u][i];
        if (!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if (instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if (dfn[u]==low[u])
    {
        Bcnt++;
        do
        {
            v=Stap[Stop--];
            instack[v]=false;
        }while (v!=u);
    }
}

void solve()
{
    int i;
    Stop=Bcnt=Dindex=0;
    memset(dfn,0,sizeof(dfn));
    memset(instack,false,sizeof(instack));
    tarjan(1);
    for (i=2; i<=n; i++)
    {
         if (!dfn[i])
         {
            Bcnt=2;
            return ;
        }
    }
}

int main()
{
    int i,a,b;
    while (scanf("%d%d",&n,&m)==2)
    {
         if (n==0 && m==0) break;
         for (int i=1; i<=n; i++) map[i].clear();
        for (int i=0; i<m; i++)
        {
            scanf("%d%d",&a,&b);
            map[a].push_back(b);
        }
        solve();
        if (Bcnt>1) printf("No\n");
        else        printf("Yes\n");
    }
    return 0;
}


 

HDOJ2767(HDOJ3836题目是一样的,改改输入方式就过了):

(先求连通块,若图已强连通,不用添边;若连通块之间完全不连通则要添加bcnt(连通块数量)条边;若有x1个连通块有入边,则添bcnt-x1条边即可;若有x2个连通块有出边,则添bcnt-x2条边即可。第一种情况特判,后三种情况即是无入边连通块数a与无出边连通块数b的最大值)

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;

const int NN=20005;
const int MM=50010;

vector<int> map[NN];
int n,m,bcnt,top,index;
int dfn[NN],low[NN],stack[NN],belong[NN],chu[NN],ru[NN];
int from[MM],to[MM];
bool instack[NN];


void init()
{
    bcnt=0;
    top=0;
    index=0;
    memset(instack,false,sizeof(instack));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
}

void tarjan(int u)
{
    dfn[u]=low[u]=++index;
    instack[u]=true;
    stack[top++]=u;
    int i,j,v;
    for (i=0; i<map[u].size(); i++)
    {
        v=map[u][i];
        if (!dfn[v])
        {
            tarjan(v);
            if (low[v]<low[u]) low[u]=low[v];
        }
        else if (instack[v] && dfn[v]<low[u]) low[u]=dfn[v];
    }
    if (low[u]==dfn[u])
    {
        bcnt++;
        do
        {
            j=stack[--top];
            instack[j]=false;
            belong[j]=bcnt;
        }while (u!=j);
    }
}

void solve()
{
    for (int i=1; i<=n; i++) if (!dfn[i]) tarjan(i);

    if (bcnt==1)
    {
        printf("0\n");
        return ;
    }

    for (int i=1; i<=bcnt; i++) ru[i]=chu[i]=0;
    for (int i=1; i<=m; i++)
    {
        int x=belong[from[i]];
        int y=belong[to[i]];
        if (x!=y)
        {
            ru[y]++;
            chu[x]++;
        }
    }

    int a,b;
    a=b=0;
    for (int i=1; i<=bcnt; i++)
    {
        if (ru[i]==0) a++;
        if (chu[i]==0) b++;
    }
    if (a>b) b=a;
    printf("%d\n",b);
}

int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d%d",&n,&m);
        init();
        for (int i=1; i<=n; i++) map[i].clear();
        for (int i=1; i<=m; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            map[a].push_back(b);
            from[i]=a;
            to[i]=b;
        }
        solve();
    }
    return 0;
}

HDOJ1827:

(求出所有无入边的连通块的最小权,这些最小权的和即答案)

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;

const int NN=1002;
const int MM=2004;
const int INF=0x7fffffff;

vector<int> map[NN];
int n,m,Dindex,top,Bcnt;
int dfn[NN],w[NN],low[NN],belong[NN],stack[NN],cost[NN];
bool instack[NN],ru[NN];

void tarjan(int u)
{
    dfn[u]=low[u]=++Dindex;
    stack[++top]=u;
    instack[u]=true;
    int v;
    for (int i=0; i<map[u].size(); i++)
    {
        v=map[u][i];
        if (!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if (instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if (low[u]==dfn[u])
    {
        Bcnt++;
        do
        {
            v=stack[top--];
            belong[v]=Bcnt;
            instack[v]=false;
        }while (v!=u);
    }
}

void solve()
{
    int u,v,i;
    memset(dfn,0,sizeof(dfn));
    memset(instack,false,sizeof(instack));
    Dindex=top=Bcnt=0;

    for (i=1; i<=n; i++) if (!dfn[i]) tarjan(i);

    memset(ru,false,sizeof(ru));
    for (u=1; u<=n; u++)
    {
        for (i=0; i<map[u].size(); i++)
        {
            v=map[u][i];
            if (belong[u]!=belong[v]) ru[belong[v]]=true;
        }
    }

    for (i=1; i<=Bcnt; i++) cost[i]=INF;
    for (i=1; i<=n; i++)
    {
        if (!ru[belong[i]])
            cost[belong[i]]=min(cost[belong[i]],w[i]);
    }

    int ans1=0;
    int ans2=0;
    for (i=1; i<=Bcnt; i++)
    {
        if (!ru[i]) { ans1++; ans2+=cost[i]; }
    }
    printf("%d %d\n",ans1,ans2);
}

int main()
{
    while (scanf("%d%d",&n,&m)==2)
    {
        for (int i=1; i<=n; i++)
        {
            scanf("%d",&w[i]);
            map[i].clear();
        }
        int a,b;
        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&a,&b);
            map[a].push_back(b);
        }
        solve();
    }
    return 0;
}

HDOJ3072(跟上题很类似,想起一些天做比赛时看到这题,居然傻了,数据范围又大~):

(求各强连通块最小入度和,注意有结点0所在连通块的最小入度为0)

#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;

const int NN=50005;
const int MM=100010;
const int INF=0x7fffffff;

struct Edge{
   int u,v,w,next;
}edge[MM];
int n,m,Ecnt,Bcnt,Dindex,top;
int st[NN],dfn[NN],low[NN],belong[NN],stack[NN],cost[NN];
bool instack[NN];

void addedge(int u,int v,int w)
{
    Ecnt++;
    edge[Ecnt].u=u; edge[Ecnt].v=v; edge[Ecnt].w=w; edge[Ecnt].next=st[u];
    st[u]=Ecnt;
}

void init()
{
     Ecnt=0;
     Dindex=top=Bcnt=0;
     int i;
     for (i=0; i<n; i++)
     {
         st[i]=dfn[i]=0;
         instack[i]=false;
     }
}

void tarjan(int u)
{
    dfn[u]=low[u]=++Dindex;
    stack[++top]=u;
    instack[u]=true;
    int i,v;
    for (i=st[u]; i; i=edge[i].next)
    {
        v=edge[i].v;
        if (!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if (instack[v])
            low[u]=min(low[u],dfn[v]);
    }
    if (low[u]==dfn[u])
    {
        Bcnt++;
        do
        {
            v=stack[top--];
            instack[v]=false;
            belong[v]=Bcnt;
        }while (v!=u);
    }
}

void solve()
{
    int i,k1,k2;
    for (i=0; i<n; i++) if (!dfn[i]) tarjan(i);

    for (i=1; i<=Bcnt; i++) cost[i]=INF;
    cost[belong[0]]=0;
    for (i=1; i<=Ecnt; i++)
    {
        k1=belong[edge[i].u]; k2=belong[edge[i].v];
        if (k1!=k2) cost[k2]=min(cost[k2],edge[i].w);
    }

    int ans=0;
    for (i=1; i<=Bcnt; i++) ans+=cost[i];

    printf("%d\n",ans);
}

int main()
{
    int i,a,b,c;
    while (scanf("%d%d",&n,&m)==2)
    {
        init();
        for (i=0; i<m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c);
        }
        solve();
    }
    return 0;
}

图论啊,图论~

共勉。

你可能感兴趣的:(c,算法,测试,System,IM)