题目链接:
题目描述:
有n个节点,m条有权单向路,要求用一个或者多个环覆盖所有的节点。每个节点只能出现在一个环中,每个环中至少有两个节点。问最小边权花费为多少?
解题思路:
因为每个节点就出现一个,那么每个节点出度和入度都为1咯。我们可以对每个节点u拆点为u,u‘,分别放在集合X,Y.然后对两个集合进行完备匹配。完备匹配成功以后,每个节点就会有只有一个出度,一个入度的。
用KM求最小匹配的话,先初始化maps为-INF,然后把各边权值存为负,求出最大值取反即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 const int maxn = 310; 7 const int INF = 0x3f3f3f3f; 8 int maps[maxn][maxn], used[maxn], s[maxn], n; 9 int lx[maxn], ly[maxn]; 10 bool visx[maxn], visy[maxn]; 11 bool Find (int x) 12 { 13 visx[x] = 1; 14 for (int i=1; i<=n; i++) 15 { 16 if (!visy[i] && lx[x]+ly[i]==maps[x][i]) 17 { 18 visy[i] = 1; 19 if (!used[i] || Find(used[i])) 20 { 21 used[i] = x; 22 return true; 23 } 24 } 25 else 26 s[i] = min (s[i], lx[x] + ly[i] - maps[x][i]); 27 } 28 return false; 29 } 30 int KM () 31 { 32 memset (used, 0, sizeof(used)); 33 memset (lx, 0, sizeof(lx)); 34 memset (ly, 0, sizeof(ly)); 35 for (int i=1; i<=n; i++) 36 for (int j=1; j<=n; j++) 37 lx[i] = max (lx[i], maps[i][j]); 38 for (int i=1; i<=n; i++) 39 { 40 for (int j=1; j<=n; j++) 41 s[j] = INF; 42 while (1) 43 { 44 memset (visx, 0, sizeof(visx)); 45 memset (visy, 0, sizeof(visy)); 46 if (Find(i)) 47 break; 48 int d = INF; 49 for (int j=1; j<=n; j++) 50 if (!visy[j]) 51 d = min (s[j], d); 52 for (int j=1; j<=n; j++) 53 { 54 if (visx[j]) 55 lx[j] -= d; 56 if (visy[j]) 57 ly[j] += d; 58 } 59 } 60 } 61 int res = 0; 62 for (int i=1; i<=n; i++) 63 res += maps[used[i]][i]; 64 return res; 65 } 66 int main () 67 { 68 int m, t; 69 scanf ("%d", &t); 70 while (t --) 71 { 72 scanf("%d %d", &n, &m); 73 for (int i=1; i<=n; i++) 74 for (int j=1; j<=n; j++) 75 maps[i][j] = -INF; 76 while (m --) 77 { 78 int u, v, s; 79 scanf ("%d %d %d", &u, &v, &s); 80 maps[u][v] = max(maps[u][v], -s; 81 } 82 printf ("%d\n", -KM()); 83 } 84 return 0; 85 }
时间: 2024-10-23 13:47:45