题目链接
题意:
某国有
n
n
个城镇,条单向铁路。每条铁路都连接着两个不同的城镇,且该铁路系统中不存在环。现需要确定一些列车运行线,使其满足:
1
1
每条铁路最多属于一条列车运行线;
2
2
每个城镇最多被一条列车运行线通过(通过包括作为起点或终点);
3
3
每个城镇至少被一条列车运行线通过;
4
4
列车运行线的数量应尽量小。
5
5
在满足以上条件下列车运行线的长度和应该尽量小。
思路:
05
05
年的论文题, 将每个点
u
u
拆成入点和出点
u
u
,对于这条有向边,
u
u
到连接一条有向边, 权值为该路径长度的负值,然后就是求一个二分图最大权匹配, 求得的最大值的负数值即为原路径长度最小值。然后找一下求解出来的路径, 一条路径如果是起点,那么它只有出点, 也就是说
u
u
是起点的话, 只有有匹配边而
u′
u
′
没有匹配边,同样
u
u
如果是终点的话只有有匹配边而
u
u
<script type="math/tex" id="MathJax-Element-3938">u</script>没有,寻找一下就好了, 注意有可能只有一个点的情况。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<iostream>
typedef long long ll;
const int maxn = 1e2 + 10;
const int INF = 1e9;
using namespace std;
typedef pair<int, int> pa;
vector<pa> G[maxn];
int w[maxn][maxn], n;
int lx[maxn], ly[maxn];
int from[maxn], m;
bool S[maxn], T[maxn];
bool match(int i) {
S[i] = true;
for(int j = 1; j <= n; j++) {
if(lx[i] + ly[j] != w[i][j] || T[j]) continue;
T[j] = true;
if(!from[j] || match(from[j])) {
from[j] = i;
return true;
}
}
return false;
}
void update() {
int a = 1 << 30;
for(int i = 1; i <= n; i++) {
if(!S[i]) continue;
for(int j = 1; j <= n; j++) {
if(T[j]) continue;
a = min(a, lx[i] + ly[j] - w[i][j]);
}
}
for(int i = 1; i <= n; i++) {
if(S[i]) lx[i] -= a;
if(T[i]) ly[i] += a;
}
}
void KM() {
for(int i = 1; i <= n; i++) {
from[i] = lx[i] = ly[i] = 0;
for(int j = 1; j <= n; j++) {
lx[i] = max(lx[i], w[i][j]);
}
}
for(int i = 1; i <= n; i++) {
while(1) {
for(int j = 1; j <= n; j++) S[j] = T[j] = 0;
if(match(i)) break;
else update();
}
}
}
int vis[maxn], to[maxn];
vector<int> vec[maxn];
int main() {
while(scanf("%d %d", &n, &m) != EOF) {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) w[i][j] = -INF;
for(int i = 1; i <= m; i++) {
int u, v, c;
scanf("%d %d %d", &u, &v, &c);
w[u][v] = -c;
}
KM();
int ans = 0, num = 0;
for(int i = 1; i <= n; i++) if(w[from[i]][i] != -INF) ans += w[from[i]][i];
memset(vis, 0, sizeof vis);
memset(to, -1, sizeof to);
for(int i = 0; i < maxn; i++) vec[i].clear();
for(int i = 1; i <= n; i++) {
if(w[from[i]][i] != -INF) {
to[from[i]] = i;
vis[i] = 1;
}
}
for(int i = 1; i <= n; i++) {
if(to[i] == -1 && !vis[i]) { vec[num].push_back(i); num++; continue; }
if(~to[i] && !vis[i]) { ///起始点
int x = i;
while(~x) {
vec[num].push_back(x);
x = to[x];
}
num++;
}
}
printf("%d %d\n", num, -ans);
for(int i = 0; i < num; i++) {
printf("%d", vec[i].size());
for(int j = 0; j < vec[i].size(); j++) printf(" %d", vec[i][j]);
printf("\n");
}
}
return 0;
}