详见 胡伯涛的:算法合集之《最小割模型在信息学竞赛中的应用》
题目大意:给出M条带权边,现在要求你选出k条割边,将点1和点n分成两个割集,且割边的总权值/k达到最小,并输出这k条割边
解题思路:求最大权值的方法论文里面有讲,这边解释一下如何找到割边
首先,得到残余网络,在残余网络里,从源点出发进行DFS,走能走的边,也就是容量-流量>0的边,并标记所走过的点
被标记的点就是S集的点了,没被标记的就是T集的点,理由如下:不能走的边就是容量-流量==0的边了,而那条边刚好是割边,将两个集合分割开的割边
所以要找出割边的话,就判断该边的起点是否属于S集,终点是否是T集即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define N 110
#define INF 0x3f3f3f3f
#define R 1000010
const double esp = 1e-6;
struct Edge {
int from, to, id;
double cap, flow;
Edge() {}
Edge(int from, int to, double cap, double flow, int id): from(from), to(to), cap(cap), flow(flow), id(id) {}
};
struct ISAP {
int p[N], num[N], cur[N], d[N];
int t, s, n, m;
bool vis[N];
vector<int> G[N];
vector<Edge> edges;
void init(int n) {
this->n = n;
for (int i = 0; i <= n; i++) {
G[i].clear();
d[i] = INF;
}
edges.clear();
}
//双向边
void AddEdge(int from, int to, double cap, int id) {
edges.push_back(Edge(from, to, cap, 0, id));
edges.push_back(Edge(to, from, cap, 0, id));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int> Q;
d[t] = 0;
vis[t] = 1;
Q.push(t);
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = 0; i < G[u].size(); i++) {
Edge &e = edges[G[u][i] ^ 1];
if (!vis[e.from] && e.cap - e.flow > esp) {
vis[e.from] = true;
d[e.from] = d[u] + 1;
Q.push(e.from);
}
}
}
return vis[s];
}
double Augment() {
int u = t;
double flow = INF;
while (u != s) {
Edge &e = edges[p[u]];
flow = min(flow, e.cap - e.flow);
u = edges[p[u]].from;
}
u = t;
while (u != s) {
edges[p[u]].flow += flow;
edges[p[u] ^ 1].flow -= flow;
u = edges[p[u]].from;
}
return flow;
}
double Maxflow(int s, int t) {
this->s = s; this->t = t;
double flow = 0;
BFS();
if (d[s] >= n)
return 0;
memset(num, 0, sizeof(num));
memset(cur, 0, sizeof(cur));
for (int i = 0; i <= n; i++)
if (d[i] < INF)
num[d[i]]++;
int u = s;
while (d[s] < n) {
if (u == t) {
flow += Augment();
u = s;
}
bool ok = false;
for (int i = cur[u]; i < G[u].size(); i++) {
Edge &e = edges[G[u][i]];
if (e.cap - e.flow > esp&& d[u] == d[e.to] + 1) {
ok = true;
p[e.to] = G[u][i];
cur[u] = i;
u = e.to;
break;
}
}
if (!ok) {
int Min = n - 1;
for (int i = 0; i < G[u].size(); i++) {
Edge &e = edges[G[u][i]];
if (e.cap - e.flow > esp)
Min = min(Min, d[e.to]);
}
if (--num[d[u]] == 0)
break;
num[d[u] = Min + 1]++;
cur[u] = 0;
if (u != s)
u = edges[p[u]].from;
}
}
return flow;
}
};
ISAP isap;
#define maxn 410
struct Cables{
int u, v, d;
}C[maxn];
int n, m;
int ans_C[maxn];
bool used[maxn], vis[maxn];
void init() {
for (int i = 1; i <= m; i++) scanf("%d%d%d", &C[i].u, &C[i].v, &C[i].d);
}
double build(double mid) {
isap.init(n);
memset(used, 0, sizeof(used));
double Sum = 0;
for (int i = 1; i <= m; i++) {
if (C[i].d - mid <= esp) {
used[i] = true;
Sum += C[i].d - mid;
}
else isap.AddEdge(C[i].u, C[i].v, C[i].d - mid, i);
}
return Sum;
}
//dfs找出从源点出发,所能遍历的所有的点,也就是S集
void dfs(int u) {
vis[u] = true;
for (int i = 0; i < isap.G[u].size(); i++) {
Edge &e = isap.edges[isap.G[u][i]];
if (e.cap - e.flow > esp) {
int v = e.to;
if (!vis[v]) dfs(v);
}
}
}
void solve() {
double l = 0.0, r = R, mid, ans;
while (r - l > esp) {
mid = (r + l) / 2;
ans = build(mid);
ans += isap.Maxflow(1, n);
if (ans > esp) l = mid;
else r = mid;
}
memset(vis, 0, sizeof(vis));
dfs(1);
//找出割边,割边要满足一点在S集,一点在T集,而S集的所有的点都是被标记
for (int i = 0; i < isap.edges.size(); i++) {
Edge &e = isap.edges[i];
int u = e.from, v = e.to;
if (vis[u] && !vis[v])
used[e.id] = true;
}
int num = 0;
for (int i = 1; i <= m; i++)
if (used[i]) ans_C[num++] = i;
printf("%d\n", num);
for (int i = 0; i < num; i++) {
if (i)
printf(" ");
printf("%d", ans_C[i]);
}
printf("\n");
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
init();
solve();
}
return 0;
}