Minimal k-covering 最大流 殘餘網路 逆向思維
阿新 • • 發佈:2018-12-12
題意:
給你一個二分圖。
問最少保留哪些邊,使得每個點的度數至少為k。k從0到mindegree。
1 <= n1, n2 <= 2000. m <= 2000
題解:
網路流。
考慮逆向思考。
我們用源點連第一部分的點,匯點連第二部分的點。
對於某一個k,這些邊的容量是點的度數-k,代表不選的原邊。
然後第一部分的點用原邊連第二部分的點,容量為1。
因為求的是最大流,所以不選邊數的最大,選的邊數最小,並且點度數都至少為k,正好符合題目條件。
對於k-1,我們只要把邊的容量+1,然後在殘餘網路上再跑網路流即可。
所以k從mindegree列舉到0。複雜度我算不清楚,題解上說是
程式碼:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <queue> #include <bitset> #include <map> #include <vector> #include <stack> #include <set> #include <unordered_map> #include <unordered_set> #include <cmath> #include <ctime> #ifdef LOCAL #define debug(x) cout<<#x<<" = "<<(x)<<endl; #else #define debug(x) 1; #endif #define chmax(x,y) x=max(x,y) #define chmin(x,y) x=min(x,y) #define lson id<<1,l,mid #define rson id<<1|1,mid+1,r #define lowbit(x) x&-x #define mp make_pair #define pb push_back #define fir first #define sec second using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<ll, int> pii; const int MOD = 1e9 + 7; const double PI = acos (-1.); const double eps = 1e-10; const int INF = 0x3f3f3f3f; const ll INFLL = 0x3f3f3f3f3f3f3f3f; const int MAXN = 2e5 + 5; const int inf = 0x3f3f3f3f; const int MX = 4111; const int MXE = 1e5; struct MaxFlow { int flow; struct Edge { int v, w, nxt; } edge[MXE]; int tot, num, s, t; int head[MX]; void init() { memset(head, -1, sizeof(head)); tot = flow = 0; } void add(int u, int v, int w) { edge[tot].v = v; edge[tot].w = w; edge[tot].nxt = head[u]; head[u] = tot++; edge[tot].v = u; edge[tot].w = 0; edge[tot].nxt = head[v]; head[v] = tot++; } int d[MX], vis[MX], gap[MX]; void bfs() { memset(d, 0, sizeof(d)); memset(gap, 0, sizeof(gap)); memset(vis, 0, sizeof(vis)); queue<int>q; q.push(t); vis[t] = 1; while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; ~i; i = edge[i].nxt) { int v = edge[i].v; if (!vis[v]) { d[v] = d[u] + 1; gap[d[v]]++; q.push(v); vis[v] = 1; } } } } int last[MX]; int dfs(int u, int f) { if (u == t) return f; int sap = 0; for (int i = last[u]; ~i; i = edge[i].nxt) { int v = edge[i].v; if (edge[i].w > 0 && d[u] == d[v] + 1) { last[u] = i; int tmp = dfs(v, min(f - sap, edge[i].w)); edge[i].w -= tmp; edge[i ^ 1].w += tmp; sap += tmp; if (sap == f) return sap; } } if (d[s] >= num) return sap; if (!(--gap[d[u]])) d[s] = num; ++gap[++d[u]]; last[u] = head[u]; return sap; } int solve(int st, int ed, int n) { //flow = 0; num = n; s = st; t = ed; bfs(); memcpy(last, head, sizeof(head)); while (d[s] < num) flow += dfs(s, inf); return flow; } } F; int eid[MAXN]; int in[MAXN]; vector<int> ans[MAXN]; int main() { #ifdef LOCAL freopen ("input.txt", "r", stdin); #endif int n1, n2, m; F.init(); scanf("%d %d %d", &n1, &n2, &m); int st = 0, en = 4050, mindeg = INF; for (int i = 1; i <= m; i++) { int u, v; scanf("%d %d", &u, &v); in[u]++; in[v + n1]++; F.add(u, v + n1, 1); eid[i + n1 + n2] = F.tot - 2; } for (int i = 1; i <= n1; i++) chmin(mindeg, in[i]); for (int i = 1; i <= n2; i++) chmin(mindeg, in[i + n1]); for (int i = 1; i <= n1; i++) { F.add(st, i, in[i] - mindeg); eid[i] = F.tot - 2; } for (int i = 1; i <= n2; i++) { F.add(i + n1, en, in[i + n1] - mindeg); eid[i + n1] = F.tot - 2; } puts("0"); for (int i = mindeg; i > 0; i--) { F.solve(st, en, en + 1); for (int j = 1; j <= n1; j++) F.edge[eid[j]].w++; for (int j = 1; j <= n2; j++) F.edge[eid[j + n1]].w++; for (int j = 1; j <= m; j++) if(F.edge[eid[j + n1 + n2]].w) ans[i].pb(j); } for (int i = 1; i <= mindeg; i++) { printf("%d ", ans[i].size()); for (int j : ans[i]) printf("%d ", j); puts(""); } return 0; }