HDU 1827 Summer Holiday(强连通分量入门好题)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1827

题意:

w i s k e y wiskey wiskey需要联系 [ 1... n ] [1...n] [1...n] n n n个人,并且 w i s k e y wiskey wiskey与这 n n n个人之间的联系需要费用,与每个人联系的费用已知这 n n n个人如果有 ( a , b ) (a,b) (a,b),即 a a a认识 b b b,那么 a a a可以联系 b b b且不需要费用,联系是单向的,且已给定一些 ( a , b ) (a,b) (a,b),问 w i s k e y wiskey wiskey需要联系到所有人,他需要联系的最少人数和最小费用

分析:

找出所有的连通量,将连通量看成点,那么只要找入度为零的连通量即可(画图模拟),然后在每个入度为0的连通量内,分别找需要费用最小的点,那么 w i s k e y wiskey wiskey只需要和这些人联系,即可;

代码:
#include<iostream>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int inf=0x7f7f7f7f;
const int maxn=2e4+50;
const int N=2e4+50;
typedef long long ll;
typedef struct{
    int u,v,next;;
}Edge;
Edge e[2*maxn];

int cnt,head[maxn];

void add(int u,int v){
    e[cnt].u=u;
    e[cnt].v=v;
    /*e[cnt].f=f;*/
    e[cnt].next=head[u];
    head[u]=cnt++;
/*  e[cnt].u=v;
    e[cnt].v=u;
    e[cnt].w=0;
    e[cnt].f=-f;
    e[cnt].next=head[v];
    head[v]=cnt++;*/
}

int read()
{
    int x = 0;
    int f = 1;
    char c = getchar();
    while (c<'0' || c>'9')
    {    
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0'&&c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x*f;
}
//a是费用,b[u]是说明u是属于哪一个连通量的,stac是栈,存连通量节点
//vis[u]为1说明u点在栈里,为0则不在,in存连通量入度,minn[u]则是第u个连通量内的费用最小值
int t,res,n,m,cost,num,a[maxn],b[maxn],dfn[maxn],low[maxn],stac[maxn],top,_time,vis[maxn],in[maxn],minn[maxn];

void tarjan(int u){
    stac[top++]=u;
    dfn[u]=low[u]=++_time;
    vis[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])low[u]=min(low[u],dfn[v]);
    }

    if(dfn[u]==low[u]){
        res++;
        do{
            t=stac[--top];
            vis[t]=0;
            b[t]=res;//标记t是属于第res个连通量的
            minn[res]=min(minn[res],a[t]);//连通量内费用最小值

        }while(t!=u);
    }
}
int main() {
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(vis,0,sizeof(vis));
        memset(head,-1,sizeof(head));
        memset(stac,0,sizeof(stac));
        memset(minn,inf,sizeof(minn));
        memset(in,0,sizeof(in));
        num=top=_time=cost=res=cnt=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int a,c;
        for(int i=0;i<m;i++){
            scanf("%d %d",&a,&c);
            add(a,c);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])tarjan(i);
        for(int i=0;i<cnt;i++){
            int u=e[i].u,v=e[i].v;
            if(b[u]!=b[v])	//u,v属于不同联通量,且u指向v则
                in[b[v]]++;	//v所在连通量入度加1
        }
        for(int i=1;i<=res;i++){
            if(!in[i])cost+=minn[i],num++;
        }
        cout<<num<<" "<<cost<<endl;
    }
}

(仅供个人理解)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值