BZOJ 4443 浅谈二分+二分图即四分图性质利用

本文探讨了一道经典的二分图题目,通过将问题转化为二分图最大匹配问题,使用二分查找和匈牙利算法求解特定条件下矩阵中元素的选择问题。

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

这里写图片描述
世界真的很大
作为二分图的一道经典题在做了这么多二分图之后还没有做过实在是太可惜了
今天讲课提到了这道题知道怎么做但是没写过总觉得心慌
然后1A,哼哼

看题先:

description:

小凸和小方是好朋友,小方给小凸一个N*M(N<=M)的矩阵A,要求小秃从其中选出N个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的N个数中第K大的数字的最小值是多少。

input如:

第一行给出三个整数N,M,K
接下来N行,每行M个数字,用来描述这个矩阵

output:

如题

方格图是一个天然的二分图模型,看到时应该想到这点
不管是行列,还是黑白染色都可以构造成二分图。
尤其是数据范围是100级别的,更应该注意这一点,不管题最后怎么说,先留下一点二分图的印象。

每一行每一列只能选一个。。越来越像二分图的模型了
想要使得选的第K大的数字最小。

每一行每一列只能选一个这个条件,限定了这张图最多只能选n个方块。
反过来说,只要还有那么多行(列)空着,总有方案能选的出来
即我们只需要选出最大的K个,让其尽量小,在保证还剩下n-K个空位的情况下,就能锁定答案。
“让大的最小”,这个让我们很容易想到二分,但是二分能处理的问题局限于“最大的最小”,而如果我们考虑前K大,那我们要求的即是说前K个”最小的最小“。这显然二分不好处理。
转化思路,既然找前K个的最小值不好,那找后n-K个的最大值岂不是就是”最大的最小“?

那我们就有了一个浅显的思路,二分这个K值,check这个K值能不能满足条件,即二分一个值,看这个值能不能作为”后n-K+1“个值里的最大值,即找不找得到n-K+1个数不再同一行同一列并且全部小于这个二分值

问题转化到此还算是清楚了。
我们想在所有值小于二分值的点里面,选最多的点,并且使得每一个点不同行,不同列,看能不能选出大于等于n-K+1个点

怎么看怎么像二分图最大匹配,行列连边。。

完整代码:

#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;

const int INF=0x3f3f3f3f;

struct edge
{
    int v,last,w;
}ed[4000010];

int n,m,K,tot=0,num=0;
int head[100010],book[100010],match[100010],mp[550][550];

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

int dfs(int u,int x)
{
    for(int i=head[u];i;i=ed[i].last)
    {//printf("*");
        int v=ed[i].v;
        if(ed[i].w>x) continue ;
        if(book[v]!=tot)
        {
            book[v]=tot;
            if(!match[v] || dfs(match[v],x))
            {
                match[u]=v,match[v]=u;
                return 1;
            }
        } 
    }
    return 0;
}

bool check(int x)
{
    memset(match,0,sizeof(match));
    memset(book,0,sizeof(book));
    int rt=0;tot=0;
    for(int i=1;i<=n+m;i++)
    {
        tot++;
        if(!match[i]) rt+=dfs(i,x);
    }
    return rt>=K;
}

int main()
{
    int lf=INF,rg=-INF,ans;
    scanf("%d%d%d",&n,&m,&K);
    K=n-K+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&mp[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            lf=min(lf,mp[i][j]),rg=max(rg,mp[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            add(i,j+n,mp[i][j]),add(j+n,i,mp[i][j]);

    while(lf<=rg)
    {
        int mid=(lf+rg)>>1;
        if(check(mid))
            ans=mid,rg=mid-1;
        else lf=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值