HDU 3072 Intelligence System(最小树形图+Tarjan)

题目链接:
HDU 3072 Intelligence System
题意:
n个点编号从0–n-1,根节点编号为0,m条有向边,属于同一有向环中的边权为0,求从根节点能到达其余所有点的最小花费?
分析:
属于同一强连通分量中边权为0.先用Tanjan算法将同一有向环中的点重新编号,然后将所有边的顶点重新编号,
并判断是否属于同一强连通分量,最后再跑一遍最小树形图即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#include <vector>
#include <stack>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, ll> pii;
const int MAX_N = 50010;
const int MAX_M = 100010;

int n, m, dfs_clock, scc_cnt;
int pre[MAX_N], In[MAX_N], ID[MAX_N], vis[MAX_N], low[MAX_N], sccno[MAX_N];
vector <int> G[MAX_N];
stack<int> S;

struct Edge{
    int u, v, w;
}edge[MAX_M];

void init()
{
    scc_cnt = dfs_clock = 0;
    for(int i = 0; i <= n; i++){
        G[i].clear();
    }
    while(!S.empty()) S.pop();
    memset(sccno, -1, sizeof(sccno));
    memset(pre, -1, sizeof(pre));
}

void dfs(int u)
{
    pre[u] = low[u] =dfs_clock++;
    S.push(u);
    for(int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if(pre[v] == -1){
            dfs(v);
            low[u] = min(low[u], low[v]);
        }else if(sccno[v] == -1){
            low[u] = min(low[u], pre[v]);
        }
    }
    if(low[u] == pre[u]){
        while(1){
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if(x == u) break;
        }
        scc_cnt++;
    }
}

void find_scc()
{
    for(int i = 0; i < n; i++){
        if(pre[i] == -1) dfs(i);
    }
}

int ZLEdmonds(int root, int NV, int NE)
{
    int res = 0, w, u, v;
    while(1){
        for(int i = 0; i < NV; i++) { In[i] = INT_MAX; }
        for(int i = 0; i < NE; i++){
            u = edge[i].u, v = edge[i].v, w = edge[i].w;
            if(u != v && w < In[v]){
                In[v] = w;
                pre[v] = u;
            }
        }
        int cnt = 0;
        memset(vis, -1, sizeof(vis));
        memset(ID, -1, sizeof(ID));
        In[root] = 0;
        for(int i = 0; i < NV; i++){
            res += In[i];
            v = i;
            while(v != root && ID[v] == -1 && vis[v] != i){
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && ID[v] == -1){
                for(u = pre[v]; u != v; u = pre[u]){
                    ID[u] = cnt;
                }
                ID[v] = cnt++;
            }
        }
        if(cnt == 0) break;
        for(int i = 0; i < NV; i++){
            if(ID[i] == -1) ID[i] = cnt++;
        }
        for(int i = 0; i < NE; i++){
            u = edge[i].u, v = edge[i].v, w = edge[i].w;
            edge[i].u = ID[u], edge[i].v = ID[v];
            if( edge[i].u != edge[i].v){
                edge[i].w -= In[v];
            }
        }
        NV = cnt, root = ID[root];
    }
     return res;
}

int main()
{
    while(~scanf("%d%d", &n, &m)){
        init();
        for(int i = 0; i < m; i++){
            scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
            G[edge[i].u].push_back(edge[i].v);
        }
        find_scc();
        for(int i = 0; i < m; i++){
            int u, v;
            u = edge[i].u, v = edge[i].v;
            edge[i].u = sccno[u], edge[i].v = sccno[v];
            if(edge[i].u == edge[i].v){
                edge[i].w = 0;
            }
        }
        int root = sccno[0];
        int ans = ZLEdmonds(root, scc_cnt, m);
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值