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