题目链接: http://codeforces.com/problemset/problem/976/F
题目大意: 给一个二分图, 可以有重边, 使得每个点覆盖次数至少为k(0~minDegree)的最小边集。 (n,m≤2000) ( n , m ≤ 2000 )
题目思路: 所谓每个点覆盖次数至少为k, 可以看成流量至多为deg[i]-k, 然后跑最大流, 剩下的边就是我们选择的边。 我们从大往小求每个k的答案, 每次只要将一些边的流量+1, 接着上一次的残留网络上跑。 我们用最朴素的方法来求最大流, 每次dfs找增广路, 每次dfs只增广一次, 一共最多做m次dfs。
Code:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define ll long long
const int N = (int)2000 + 10;
int n1, n2, m, s, t;
int cnt = 1, lst[N * 2], to[N * 10], nxt[N * 10], f[N * 10];
int deg[N * 2], ans[N][N];
int indx, tag[N * 2];
void add(int u, int v, int flow){
nxt[++ cnt] = lst[u]; lst[u] = cnt; to[cnt] = v; f[cnt] = flow;
nxt[++ cnt] = lst[v]; lst[v] = cnt; to[cnt] = u; f[cnt] = 0;
}
int dfs(int u){
if (u == t) return 1;
tag[u] = indx;
for (int j = lst[u]; j; j = nxt[j]){
int v = to[j];
if (!f[j] || tag[v] == indx) continue;
if (dfs(v)){
f[j] --; f[j ^ 1] ++;
return 1;
}
}
return 0;
}
int main(){
scanf("%d%d%d", &n1, &n2, &m);
for (int i = 1, u, v; i <= m; i ++){
scanf("%d %d", &u, &v);
add(u, n1 + v, 1); deg[u] ++; deg[n1 + v] ++;
}
int T = deg[1];
for (int i = 2; i <= n1 + n2; i ++)
T = min(T, deg[i]);
s = n1 + n2 + 1, t = s + 1;
for (int i = 1; i <= n1; i ++)
add(s, i, deg[i] - T);
for (int i = 1; i <= n2; i ++){
add(n1 + i, t, deg[n1 + i] - T);
}
for (int cvr = T; cvr >= 0; cvr --){
indx ++;
while(dfs(s)) indx ++;
for (int i = 1; i <= m; i ++)
if (f[2 * i]) ans[cvr][++ ans[cvr][0]] = i;
for (int i = 1; i <= n1; i ++)
f[2 * (m + i)] ++;
for (int i = 1; i <= n2; i ++)
f[2 * (m + n1 + i)] ++;
}
for (int i = 0; i <= T; i ++){
printf("%d ", ans[i][0]);
for (int j = 1; j <= ans[i][0]; j ++)
printf("%d ", ans[i][j]);
puts("");
}
return 0;
}