題目連結:
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;}