HDU 1827 Summer Holiday 强连通分量缩点

Summer Holiday
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
Submit  Status

Description

To see a World in a Grain of Sand 
And a Heaven in a Wild Flower, 
Hold Infinity in the palm of your hand 
And Eternity in an hour. 
                  ―― William Blake 

听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗? 
 

Input

多组测试数组,以EOF结束。 
第一行两个整数N和M(1<=N<=1000, 1<=M<=2000),表示人数和联系对数。 
接下一行有N个整数,表示Wiskey联系第i个人的电话费用。 
接着有M行,每行有两个整数X,Y,表示X能联系到Y,但是不表示Y也能联系X。 
 

Output

输出最小联系人数和最小花费。 
每个CASE输出答案一行。 
 

Sample Input

      
      
      
      
12 16 2 2 2 2 2 2 2 2 2 2 2 2 1 3 3 2 2 1 3 4 2 4 3 5 5 4 4 6 6 4 7 4 7 12 7 8 8 7 8 9 10 9 11 10
 

Sample Output

      
      
      
      
3 6
 


题意:有一个有向图,从外界发送信息给每一个结点i的花费是cost[i],问向哪些结点发送信息可以使每个节点都收到信息并且花费最少。


首先,要利用强连通分量中的结点互相可达这个特点,我们可以知道,我们只需要向某些强连通分量中某个节点发送就可以了,那么会得到两个问题。

1.向哪些强连通分量发送信息

2.向一个强连通分量中的那个结点发送信息


对于第一个问题,我们可以将一个强连通分量视为一个结点,那么如果没有其它结点指向这个结点,即这个结点的入度为0,那么就必须向这个结点发送信息。

对于第二个问题,我们很显然会选择一个花费最小的结点,向它发送信息,也就是说对于每一个连通分量,我们需要知道其内部结点的最小花费是多少,这个可以在Tarjan中更新。


#include <iostream>
#include <cmath>
#include <stdio.h>
#include <string>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <algorithm>
#define INF 9999999
#define MAXN 1010
#define MAXM 2010
using namespace std;

struct Edge
{
    int to,next;
};
//邻接表
Edge edge[2010];
int head[1010];
int tol;
//Tarjan
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int scc;
bool Instack[MAXN];
//全局
int Min[MAXN];
int cost[MAXN];
int from[2010];
int to[2010];
int indeg[1010];
int n,m;

void init()
{

    memset(indeg,0,sizeof(indeg));
    memset(head,-1,sizeof(head));
    tol=0;
}
void addedge(int u,int v)
{
    edge[tol].to=v;
    edge[tol].next=head[u];
    head[u]=tol++;
}



void Tarjan(int u)
{
    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(!DFN[v])
        {
            Tarjan(v);
            if(Low[u]>Low[v])
                Low[u]=Low[v];
        }
        else if(Instack[v]&&Low[u]>DFN[v])
            Low[u]=DFN[v];
    }
    if(Low[u]==DFN[u])//找到一个强连通分量
    {
        scc++;
        do
        {
            v=Stack[--top];
            Instack[v]=false;
            Belong[v]=scc;//v节点属于强连通分量scc
            //cout<<cost[v]<<' '<<Min[scc]<<endl;
            if(cost[v]<Min[scc])//更新scc这个强连通分量中的最小值
                Min[scc]=cost[v];
        }
        while(v!=u);
    }
}



int main()
{
    int i;
    int u,v;
    int sum;
    int scc0;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        sum=0;
        scc0=0;
        for(i=1; i<=n; i++)
            scanf("%d",&cost[i]);
        for(i=1; i<=m; i++)
        {
            scanf("%d%d",&u,&v);
            from[i]=u;
            to[i]=v;
            addedge(u,v);
        }
        for(i=1; i<=n; i++)
        {
            Min[i]=INF;
        }
        memset(DFN,0,sizeof(DFN));
        memset(Instack,false,sizeof(Instack));
        Index=scc=top=0;
        for(i=1; i<=n; i++)
        {
            if(!DFN[i])
            {
                //cout<<i<<endl;
                Tarjan(i);
            }
        }
/*
        cout<<scc<<endl;
        for(i=1; i<=scc; i++)
            cout<<cost[i]<<' ';
        cout<<endl;
*/
        for(i=1; i<=m; i++)
        {
            if(Belong[from[i]]!=Belong[to[i]])
            {
                indeg[Belong[to[i]]]++;
            }

        }
        for(i=1; i<=scc; i++)
        {
            if(indeg[i]==0)
            {
                scc0++;
                sum+=Min[i];
            }

        }
        printf("%d %d\n",scc0,sum);

    }
    return 0;
}


你可能感兴趣的:(summer,HDU,holiday,强连通分量缩点,1827)