hdu1827强连通+缩点

思路:利用tarjan算法,记录每个点所属的连通分支,然后计算每个SCC的入度,最后计算每个SCC(入度为0)中花费最小的值 
//强连通+缩点
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<string.h>
#define max 1111
using namespace std;
int N,M;
int X,Y,SCC;
int tem;
int n;
vector<int>gra[max];
stack<int>sta;
int num[max],vis[max];
int dfn[max],low[max];
int cost[max];
int belong[max];
int ind[max];
int MIN(int a,int b)
{
    return a>b?b:a;
}
void  tarjan(int x,int p)
{
    vis[x]=2;
    dfn[x]=low[x]=++n;
    sta.push(x);
    for(int i=0; i<gra[x].size(); i++)
    {
        int t=gra[x][i];
        if(!dfn[t])
        {
            tarjan(t,x);
            low[x]=MIN(low[x],low[t]);
        }
        else if(vis[t]==2)
        {
            low[x]=MIN(low[x],dfn[t]);
        }
    }
    int min=99999999;
    if(low[x]==dfn[x])
    {
        tem++;
        while(!sta.empty())
        {
            int m=sta.top();
            sta.pop();
            vis[m]=1;
            belong[m]=tem;//属于一个连通分支。
            ind[m]=0;//入度为0,刚开始设置为0
           if(x==m)
                break;
        }
    }
}
int main()
{
    int a,b;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        SCC=tem=X=Y=n=0;
        memset(num,0,sizeof(num));
        memset(vis,0,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        for(int i=0; i<max; i++)
            gra[i].clear();
        while(!sta.empty())
            sta.pop();
        for(int i=1; i<=N; i++)
            scanf("%d",&num[i]);
        for(int i=0; i<M; i++)
        {
            scanf("%d%d",&a,&b);
            gra[a].push_back(b);
        }
        for(int i=1; i<=N; i++)
            if(!dfn[i])
                tarjan(i,0);
        for(int j=1;j<=N;j++)
        for(int i=0;i<gra[j].size();i++)
        {
            int gh=gra[j][i];
            if(belong[j]!=belong[gh])//判断是不是属于一个连通分量,如果不是那么ind[belong[gh]]的入度+1
              ind[belong[gh]]++;//让第belong[gh]的入度加1;

        }
        for(int i=1;i<=tem;i++)//tem连通分支的个数
        {
            if(ind[i]==0)//连通分支的入度为0,即可找到其值
              SCC++;
            cost[i]=99999999;//将每个连通分量的值设为999999999
        }
        for(int i=1;i<=N;i++)//枚举所有点
        {
            if(ind[belong[i]]==0)//判断当前的点是不是在一个入度为0的联通分支中
             cost[belong[i]]=min(cost[belong[i]],num[i]);//求话费最小的值
        }
        for(int i=1;i<=tem;i++)
         if(cost[i]!=99999999)
           Y+=cost[i];
        printf("%d %d\n",SCC,Y);
    }
    return 0;
}

你可能感兴趣的:(hdu1827强连通+缩点)