给一个无向有权图, 选定一个边集, 使得平均边权最小。
先二分平均边权, 假设为x,构建新图,边权为原来边权减去x,如果边权小于0的话就是一定要选的, 所以可以不加入新图, 然后在新图上跑最大流, 求出最小割。
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> using namespace std; #define LL long long #define inf 0x3f3f3f3f #define mnx 102 #define mxe 2020 #define eps 1e-5 #define ls (i << 1) #define rs (ls | 1) #define md ((ll + rr) >> 1) int n, m; int from[mxe], To[mxe], val[mxe]; bool sel[mxe]; int dcmp(double x) { if(fabs(x) <= eps) return 0; return x < 0? -1: 1; } struct dinic { int fst[mnx], nxt[mxe], to[mxe], e; int s, t; double cap[mxe], flow[mxe]; int d[mnx], cur[mnx]; bool vis[mnx]; vector<int> ans; void init() { memset(fst, -1, sizeof fst); e = 0; } void add(int u, int v, double c) { to[e] = v, nxt[e] = fst[u], cap[e] = c, flow[e] = 0, fst[u] = e++; to[e] = u, nxt[e] = fst[v], cap[e] = 0, flow[e] = 0, fst[v] = e++; } bool bfs(bool flag = 0) { memset(d, 0x3f, sizeof d); queue<int> q; q.push(s); d[s] = 0; while(!q.empty()) { int u = q.front(); q.pop(); if(flag) vis[u] = 1; for(int i = fst[u]; ~i; i = nxt[i]) { int v = to[i]; if(dcmp(cap[i] - flow[i]) > 0 && d[v] == inf) { d[v] = d[u] + 1; q.push(v); } } } return d[t] != inf; } double dfs(int x, double a) { if(x == t || dcmp(a) <= 0) return a; double f, ret = 0; for(int &i = cur[x]; ~i; i = nxt[i]) { int v = to[i]; if(d[v] == d[x] + 1 && dcmp(f = dfs(v, min(a, cap[i] - flow[i]))) > 0) { a -= f; flow[i] += f; flow[i ^ 1] -= f; ret += f; if(dcmp(a) <= 0) break; } } return ret; } double go(int s, int t) { this -> s = s, this -> t = t; double ret = 0; while(bfs()) { for(int i = 1; i <= n; ++i) cur[i] = fst[i]; ret += dfs(s, inf); } return ret; } void getAns() { memset(vis, 0, sizeof vis); bfs(1); for(int i = 1; i <= m; ++i) { int u = from[i], v = To[i]; if(vis[u] ^ vis[v] == 1 && !sel[i]) ans.push_back(i); } sort(ans.begin(), ans.end()); printf("%d\n", ans.size()); for(int i = 0; i < ans.size(); ++i) printf("%d%c", ans[i], i == ans.size() - 1? '\n': ' '); } }go; bool check(double x, bool flag = 0) { go.init(); double res = 0; for(int i = 1; i <= m; ++i) { if(dcmp(val[i] - x) <= 0) { res += val[i] - x; if(flag) { go.ans.push_back(i); sel[i] = 1; } } else go.add(from[i], To[i], val[i] - x), go.add(To[i], from[i], val[i] - x); } res += go.go(1, n); return res > 0; } int main() { while(scanf("%d%d", &n, &m) != EOF) { for(int i = 1; i <= m; ++i) { scanf("%d%d%d", &from[i], &To[i], &val[i]); } memset(sel, 0, sizeof sel); double l = 0, r = 1e8; while(r - l > 1e-8) { double mid = (l + r) / 2; if(check(mid)) l = mid; else r = mid; } go.ans.clear(); check(l, 1); go.getAns(); } return 0; }