【题解】[SCOI2015]小凸玩矩阵

题目链接

思路:题目要求变相解答一下,求出是否有n-k个数,不大于当前求的第k个数

而每一行每一列只能有一个数,就可以得到一个二分图的思路,边上的权值就是第i行第j列这个数的值

对于答案就是第k大的数,则用二分来求

每一次对mid进行判断时,要重建图,以满足要求

#include <cstdio>
#include <iostream>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

#define clr(x) memset(x,0,sizeof(x))
#define int long long

inline int read(){
    int s=0;bool flag=true;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')flag=false;ch=getchar();}
    while(isdigit(ch)){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return flag?s:-s;
}

inline void out_put(int x){
    if(x<0)    putchar('-'),x=-x;
    if(x>9)    out_put(x/10);
    putchar(x%10+'0');
}

inline void print(int x){out_put(x),puts("");}

const int N=255;
const int inf=0x3f3f3f3f3f3f3f3f3f;
bool vis[N],line[N][N];
int row[N],w[N][N];
int n,m,k;

inline bool find(int x){
    for(int i=1;i<=m;i++)
        if(line[x][i] && !vis[i]){
            vis[i]=true;
            if(!row[i] || find(row[i])){
                row[i]=x;
                return true;
            }
        }
    return false;
}

inline void rebuild(int limit){
    clr(line),clr(row);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(w[i][j]<=limit)    
                line[i][j]=true;
}

inline bool check(int x){
    rebuild(x);
    int cnt=0;
    for(int i=1;i<=n;i++){
        clr(vis);
        if(find(i))    cnt++;
    }
    if(cnt>n-k)    return true;
    return false;
}

signed main(void){
    n=read(),m=read(),k=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            w[i][j]=read();
    int l=1,r=inf,ans;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid-1,ans=mid;
        else l=mid+1;
    }
    print(ans);
    return 0;
} 

 

posted @ 2020-05-08 21:59  初学者Ming  阅读(194)  评论(1编辑  收藏  举报