[SCOI2015]小凸玩矩阵

题目描述

小凸和小方是好朋友,小方给了小凸一个 n × m (的矩阵 A,并且要求小凸从矩阵中选出 nn 个数,其中任意两个数都不能在同一行或者同一列。现在小凸想知道,选出的 n 个数中第 k 大的数的最小值是多少。

输入格式

第 1 行读入 3 个整数 n, m, k

接下来 nn 行,每一行有 m 个数字,第 i 行第 j 个数字代表矩阵中第 i 行第 j 列的元素Ai,j

输出格式

输出包含一行,为选出的 n 个数中第 k 大数的最小值。


我们反过来想想

 

求第K大的最小值,就是说对第K大的数二分
就是在限制条件下,跑二分图
满足至少n-k+1个匹配的最小值
#include<bits/stdc++.h>
#define re return
#define inc(i,l,r) for(int i=l;i<=r;++i)
using namespace std;
template<typename T>inline void rd(T&x)
{
    char c;bool f=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
    x=c^48;
    while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
    if(f)x=-x;
}
const int maxn=255;
int match[maxn],n,m,k,K,hd[maxn],ans=2147483647;
int a[maxn][maxn],vis[maxn],tot;

inline bool dfs(int limit,int x)
{
    inc(i,1,m)
    if(a[x][i]>limit||vis[i]==tot);
    else
    {
        vis[i]=tot;
        if(!match[i]||dfs(limit,match[i]))
        {
            match[i]=x;
            re 1;
        }
    } 
    re 0;
}

inline int vivi(int x)
{
    inc(j,1,m)vis[j]=match[j]=0;
    int cnt=0;
    inc(i,1,n)
    {
        ++tot;
        if(dfs(x,i))++cnt;
        inc(j,1,m)vis[j]=0;
    }    
    re cnt;
}

int main() 
{
    int l=2147483647,r=0;
    rd(n),rd(m);
    rd(K);
    K=n-K+1;
    inc(i,1,n)inc(j,1,m)
    {
        rd(a[i][j]);
        l=min(l,a[i][j]);
        r=max(r,a[i][j]);
    }
    
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(vivi(mid)>=K)ans=mid,r=mid-1;
        else l=mid+1;
    }
    
    printf("%d",ans);
    re 0;
}

 

 
 
posted @ 2019-08-16 20:13  凉如水  阅读(172)  评论(0编辑  收藏  举报