HDU 3072 强连通分量 + topo

HDU 3072
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3072
题意:
给一个有向图,边有权值。
问最小的传递信息代价,代价是边的权值。
一个相互可达的顶点之间交换信息是不需要代价的,肯定存在一个点能把信息传递到其余所有点。
思路:
强连通分量+topo排序,强连通缩点以后找到入度为0的点进行topo,保存信息传递到每个新点用的最小代价即可。
主要是题目描述太丑难以理解。
源码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <stack>
#include <iostream>
#include <iostream>
#include <vector>
#include <utility>
#include <queue>
using namespace std;
#define LL long long
#define inf (1000000000)
const int MAXN = 50000;
int head1[MAXN], cnt1;
int head2[MAXN], cnt2;
struct Edge
{
    int u, v;
    int ne, val;
    Edge(){}
    Edge(int _u, int _v, int _val){u = _u, v = _v, val = _val;}
}edge1[100000 * 2 + 5], edge2[100000 * 2 + 5];
void add_edge1(int u, int v, int val)
{
    edge1[cnt1] = Edge(u, v, val);
    edge1[cnt1].ne = head1[u];
    head1[u] = cnt1++;
}
void add_edge2(int u, int v, int val)
{
    edge2[cnt2] = Edge(u, v, val);
    edge2[cnt2].ne = head2[u];
    head2[u] = cnt2++;
}
int low[MAXN], pre[MAXN], dfs_clock;
int vis[MAXN];
int scc_cnt, sccno[MAXN];
stack<int>sta;
void tarjan_scc(int u, int p)
{
    low[u] = pre[u] = ++dfs_clock;
    sta.push(u);
    for(int now = head1[u] ; now != -1 ; now = edge1[now].ne){
        int v = edge1[now].v;
        if(pre[v] == 0){
            tarjan_scc(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(!sccno[v]){
            low[u] = min(low[u], pre[v]);
        }
    }
    if(low[u] == pre[u]){
        scc_cnt++;
        while(!sta.empty()){
            int org = sta.top();    sta.pop();
            sccno[org] = scc_cnt;
            if(org == u)    break;
        }
    }
}
bool cmp2(Edge a, Edge b){return a.val < b.val;}
int in[MAXN];
int cost[MAXN];
queue<int>que;
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        memset(head1, -1, sizeof(head1));
        cnt1 = 0;
        int u, v, w;
        for(int i = 0 ; i < m ; i++){
            scanf("%d%d%d", &u, &v, &w);
            add_edge1(u, v, w);
        }
        memset(pre, 0, sizeof(pre));
        memset(low, 0, sizeof(low));
        dfs_clock = 0;
        memset(sccno, 0, sizeof(sccno));
        scc_cnt = 0;
        while(!sta.empty()) sta.pop();
        for(int i = 0 ; i < n ; i++){
            if(pre[i] == 0){
                tarjan_scc(i, -1);
            }
        }
        memset(head2, -1, sizeof(head2));
        cnt2 = 0;
        memset(in, 0, sizeof(in));
        for(int i = 0 ; i < cnt1 ; i++){
            u = edge1[i].u, v = edge1[i].v;
            w = edge1[i].val;
            if(sccno[u] != sccno[v]){
                add_edge2(sccno[u], sccno[v], w);
                in[sccno[v]]++;
            }
        }
        memset(vis, 0, sizeof(vis));
        LL ans = 0;
        for(int i = 1 ; i <= scc_cnt ; i++)
            cost[i] = inf;
        for(int i = 1 ; i <= scc_cnt ; i++){
            if(in[i] == 0){
                u = i;
                break;
            }
        }
        while(!que.empty()) que.pop();
        que.push(u);
        cost[u] = 0;
        while(!que.empty()){
            u = que.front();    que.pop();
            for(int now = head2[u] ; now != -1 ; now = edge2[now].ne){
                int v = edge2[now].v;
                in[v]--;
                cost[v] = min(cost[v], edge2[now].val);
                if(in[v] == 0)  que.push(v);
            }
        }
        for(int i = 1 ; i <= scc_cnt ; i++)
            ans += cost[i];
//        sort(edge2, edge2 + cnt2, cmp2);
//        for(int i = 0 ; i < cnt2 ; i++){
//            u = edge2[i].u, v = edge2[i].v;
//            if(vis[u] == 0 || vis[v] == 0){
//                ans += edge2[i].val;
//                if(vis[u] == 0) vis[u] = 1, cnt++;
//                if(vis[v] == 0) vis[v] = 1, cnt++;
//            }
//            if(cnt == scc_cnt)
//                break;
//        }
        printf("%I64d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值