HDOJ 3072 Intelligence System

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3072

题意:给我们一些边的关系和权值,让我们从0开始遍历所有点,问我们最少花费多少,起初以为是最短路问题,是因为小编在读题的时候没有注意到两句话

if two people can inform each other, directly or indirectly through someone else, then they belong to the same branch

 the cost between persons in the same branch will be ignored

这两句话很关键啊,小编来翻译一下:如果两个人可以直接或间接到达对方他们就属于一个部门,而一个部门的不用计算他们到达对方的花费。
这两句话可以说直接改变了整个题的做题思路(强行加入了一波强连通……),也就是说如果两个点属于一个强连通分量那么他们之间的花费就是,那么问题就变成了强连通分量之间的到达花费问题,所以我们上来第一步肯定是Tarjan算法缩点建立DAG图。
建好图后又该怎么做呢?小编一开始想的是用带权值的二分图来计算,但是很明显像小编这样做肯定麻烦了,因为题意很明显是可以从0遍历到所有点的,那么一定0所在的分量一定入度为0,小编解释一下,如果0的所在的分量的入度不为0,那么由于从0一定能遍历到所有点,那么0所在的分量也一定可以到达这个能到达0所在的分量的分量,也就是说一定可以互相到达,那么就这两个分量就可以合并为一个分量,就和两个分量的存在矛盾,所以0所在的分量的入度一定为0。又因为可以遍历到所有点,那么如果二分图解决的话一定是完美匹配……(那还二分个毛),所以这里我们直接采取贪心就好了,刚刚解释了为什么0所在的分量入度为0,现在大家再想想入度为0的有几个?当然肯定只有一个,那就是0所在的分量,知道了这些,我们直接采取每一个点都选到它花费最少的边走能达到最小的方法,因为只有一个0所在的分量的入度为0,那么这样是一定最小,且一定也可以建立成一个完整的图的。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <stack>
using namespace std;
const int N=50006;
struct Edge
{
    int v, val, next;
    Edge() {}
    Edge(int a, int b, int c)
    {
        v=a, val=b, next=c;
    }
} edge[100006];
int head[N], tot;
int n, m;
void add_edge(int st, int en, int val)
{
    edge[tot]=Edge(en, val, head[st]);
    head[st]=tot++;
}
int DFN[N], Low[N], index, num, belong[N], in[N];
bool instack[N];
stack<int> S;
void Tarjan(int u)
{
    S.push(u), instack[u]=true;
    DFN[u] = Low[u] = index++;
    for(int e=head[u]; e!=-1; e=edge[e].next)
    {
        int v=edge[e].v;
        if(DFN[v]==-1)
        {
            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])
    {
        for(int v; 1; )
        {
            v=S.top();
            belong[v] = num;
            instack[v]=false, S.pop();
            if(v == u) break;
        }
        num++;
    }
}
int main()
{
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        memset(head, -1, sizeof head);
        tot=0;
        for(int i=0, a, b, c; i<m; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            add_edge(a, b, c);
        }
        while(!S.empty()) S.pop();
        memset(instack, 0, sizeof instack);
        memset(DFN, -1, sizeof DFN);
        index = num = 0;
        for(int i=0; i<n; i++)
			if(DFN[i]==-1)
				Tarjan(i);
        for(int i=0; i<num; i++) in[i]=0x3fffffff;
        for(int i=0; i<n; i++)
        {
            int u = belong[i];
            for(int e=head[i]; e!=-1; e=edge[e].next)
            {
                int v = belong[edge[e].v];
                if(u != v) in[v]=min(in[v], edge[e].val);
            }
        }
        int ans=0;
        for(int i=0; i<num; i++)
        {
            if(i == belong[0]) continue;
            ans += in[i];
        }
        printf("%d\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值