题意:给一个二分图,存在重边,求使各点度数至少为k(0<=k<=min(d[i]))的最小边集合。
分析:首先建立一个源点s向二分图左边连边,建立一个汇点e,所有二分图右边的点向e点连边。要使每个点的度数大于等于k,可使流过每个点的流量小于等于d[i]-k。(d[i]为每个点的度数),就可以在建立源点到二分图左边点,和二分图右边点到汇点时开始限制。然后对每个k跑一遍最大流,记录一下剩余的边即是正确答案。
#include<bits/stdc++.h>
using namespace std;
const int N=4010;
vector<int>g[N],num[N],ans[N];
int f[N][N],tem[N][N],n,m,d[N],cnt,v[N];
bool dfs(int x)
{
if(x==n)return true;
v[x]=cnt;
for(int i=0;i<g[x].size();i++)
{
int y=g[x][i];
if(f[x][y]==0||v[y]==cnt)continue;
if(dfs(y))
{
f[x][y]--;
f[y][x]++;
return true;
}
}
return false;
}
int main()
{
int n1,n2,x,y,k;
scanf("%d%d%d",&n1,&n2,&k);
n=n1+n2+1;
for(int i=1;i<=k;i++)
{
scanf("%d%d",&x,&y);
y+=n1;
g[x].push_back(y);
g[y].push_back(x);
num[x].push_back(i);
f[x][y]++;
d[x]++,d[y]++;
}
m=k*2;
for(int i=1;i<n;i++)m=min(m,d[i]);
for(int i=1;i<=n1;i++)
{
g[0].push_back(i);
g[i].push_back(0);
f[0][i]=d[i]-m;
}
for(int i=n1+1;i<n;i++)
{
g[i].push_back(n);
g[n].push_back(i);
f[i][n]=d[i]-m;
}
for(int l=m;l>=0;l--)
{
cnt++;
while(dfs(0))cnt++;
for(int i=1;i<=n1;i++)
{
for(int j=0;j<g[i].size();j++)
{
y=g[i][j];
tem[i][y]=f[i][y];
}
}
for(int i=1;i<=n1;i++)
{
for(int j=0;j<g[i].size();j++)
{
y=g[i][j];
if(y&&tem[i][y])
{
tem[i][y]--;
ans[l].push_back(num[i][j]);
}
}
}
for(int i=1;i<=n1;i++)f[0][i]++;
for(int i=n1+1;i<n;i++)f[i][n]++;
}
for(int i=0;i<=m;i++)
{
printf("%d",ans[i].size());
for(auto &x:ans[i])
printf(" %d",x);
printf("\n");
}
}