题目链接:
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;
}