Codeforces 976F Minimal k-covering【最大流】

本文介绍了一种算法,用于解决给定带有重边的二分图中,寻找使得各点度数至少为k的最小边集合问题。通过构建网络流模型,并对每个可能的k值运行最大流算法来确定最终的边集合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接

题意:给一个二分图,存在重边,求使各点度数至少为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");
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值