zoukankan      html  css  js  c++  java
  • HDU 1827 Summer Holiday(tarjan求强连通分量+缩点构成新图+统计入度+一点贪心思)经典缩点入门题

    Summer Holiday

    Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 4574    Accepted Submission(s): 2078


    Problem 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
     
    Author
    威士忌
     
    Source
     
    分析:
    1.属于同一个强连通分量的点之间可以互相到达,所以可以缩成一个点,
    2.缩点之后就构成了一个新图,统计新图入度为0的点,该点就是需要通知的人数
    3.花费就是该点的强连通分量里面选择一个价值最小的点(同一个颜色的点中选择价值最小的点)
    染色缩点!!!,学习了
    code:
     
    #include<stdio.h>
    #include<iostream>
    #include<math.h>
    #include<string.h>
    #include<set>
    #include<map>
    #include<list>
    #include<math.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    #define INF 0x7fffffff
    int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
    int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
    int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};
    
    int getval()
    {
        int ret(0);
        char c;
        while((c=getchar())==' '||c=='
    '||c=='
    ');
        ret=c-'0';
        while((c=getchar())!=' '&&c!='
    '&&c!='
    ')
            ret=ret*10+c-'0';
        return ret;
    }
    
    #define max_v 1005
    #define mem(a,x) memset(a,x,sizeof(a))
    vector<int> G[max_v];
    int dfn[max_v];
    int low[max_v];
    int vis[max_v];
    int indgree[max_v];
    int color[max_v];
    int stk[max_v];
    int v[max_v];
    int sig,t,cnt;
    int n,m;
    
    void init()
    {
        for(int i=1;i<=n;i++)
                G[i].clear();
        mem(dfn,0);
        mem(low,0);
        mem(vis,0);
        mem(indgree,0);
        mem(stk,0);
        mem(v,0);
        mem(color,0);
        sig=0;//强连通分量数
        t=-1;//栈顶指针
        cnt=1;//深度标识
    }
    
    void tarjan(int u)
    {
        vis[u]=1;
        dfn[u]=low[u]=cnt++;
        stk[++t]=u;//入栈
    
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(vis[v]==0)
                tarjan(v);
            if(vis[v]==1)
                low[u]=min(low[u],low[v]);
        }
        if(dfn[u]==low[u])
        {
            sig++;
            do
            {
                vis[stk[t]]=-1;//染色标记
                color[stk[t]]=sig;//将该强连通分量的点染成一个颜色
            }while(stk[t--]!=u);//栈结构储存需要染色的点
        }
    }
    
    int f(int x)//统计属于x颜色的点中价值最小的点的价值
    {
        int minv=INF;
        for(int i=1;i<=n;i++)
        {
            if(color[i]==x)
            {
                minv=min(minv,v[i]);
            }
        }
        return minv;
    }
    int main()
    {
        int x,y;
        while(~scanf("%d %d",&n,&m))
        {
            init();
            for(int i=1;i<=n;i++)
                scanf("%d",&v[i]);
            for(int i=1;i<=m;i++)
            {
                scanf("%d %d",&x,&y);
                if(count(G[x].begin(),G[x].end(),y)==0)//重边
                    G[x].push_back(y);
            }
    
            //tarjan求强连通分量
            for(int i=1;i<=n;i++)
            {
                if(vis[i]==0)
                    tarjan(i);
            }
    
            //统计缩点之后新图的点的入度
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<G[i].size();j++)
                {
                    int v=G[i][j];
                    if(color[v]!=color[i])
                        indgree[color[v]]++;
                }
            }
    
            int ans=0,cost=0;
            for(int i=1;i<=sig;i++)
            {
                if(indgree[i]>0)//只有入读为0的点需要打电话
                    continue;
                ans++;
                cost+=f(i);
            }
            printf("%d %d
    ",ans,cost);
        }
        return 0;
    }
     
    心之所向,素履以往
  • 相关阅读:
    【2019-12-13】泛型
    【2019-12-12】函数
    【2019-12-10】类
    【2019-12-05】接口
    【2019-12-3】变量声明
    【2019-11-24】基础类型
    【2019-11-20】服务与DI简介
    【2019-11-20】组件简介
    android之ListView与Adapter(结合JavaBean)
    android基类Adapter
  • 原文地址:https://www.cnblogs.com/yinbiao/p/9946708.html
Copyright © 2011-2022 走看看