BZOJ4443: [Scoi2015]小凸玩矩阵

Description

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

 

Input

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

 

Output

如题 
 

 

Sample Input

3 4 2
1 5 6 6
8 3 4 3
6 8 6 3

Sample Output

3

HINT

 

1<=K<=N<=M<=250,1<=矩阵元素<=10^9

 
二分答案x,然后“存在方案使得第K大的数<=x”等价于“存在方案选n-k+1个数<=x”。
那么二分图匹配即可。
#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=510;
const int maxm=200010;
const int inf=1e9;
struct ISAP{
    struct tedge{int x,y,w,next;}adj[maxm];int ms,fch[maxn];
    int d[maxn],s[maxn],cur[maxn],gap[maxn],n,top;
    void init(int n){
        this->n=n;ms=0;top=0;
        memset(d,-1,sizeof(d));
        memset(fch,-1,sizeof(fch));
        return;
    }
    void AddEdge(int u,int v,int w){
        adj[ms]=(tedge){u,v,w,fch[u]};fch[u]=ms++;
        adj[ms]=(tedge){v,u,0,fch[v]};fch[v]=ms++;
        return;
    }
    void bfs(){
        queue<int>Q;Q.push(n);d[n]=0;
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=fch[u];i!=-1;i=adj[i].next){
                int v=adj[i].y;
                if(d[v]==-1) d[v]=d[u]+1,Q.push(v);
            }
        } return;
    }
    int solve(int S,int T){
        n=T;bfs();int k=S,i,flow=0;
        for(i=0;i<=n;i++) cur[i]=fch[i],gap[d[i]]++;
        while(d[S]<n){
            if(k==n){
                int mi=inf,pos;
                for(i=0;i<top;i++) if(adj[s[i]].w<mi) mi=adj[s[i]].w,pos=i;
                for(i=0;i<top;i++) adj[s[i]].w-=mi,adj[s[i]^1].w+=mi;
                flow+=mi;top=pos;k=adj[s[top]].x;
            }
            for(i=cur[k];i!=-1;i=adj[i].next){
                int v=adj[i].y;
                if(adj[i].w&&d[k]==d[v]+1){cur[k]=i;k=v;s[top++]=i;break;}
            }
            if(i==-1){
                int lim=n;
                for(i=fch[k];i!=-1;i=adj[i].next){
                    int v=adj[i].y;
                    if(adj[i].w&&d[v]<lim) lim=d[v],cur[k]=i;
                } if(--gap[d[k]]==0) break;
                d[k]=lim+1;gap[d[k]]++;
                if(k!=S) k=adj[s[--top]].x;
            }
        } return flow;
    }
}sol;
int n,m,k,val[maxn][maxn];
int check(int x) {
    int S=n+m+1,T=n+m+2;sol.init(T);
    rep(i,1,n) sol.AddEdge(S,i,1);
    rep(i,1,m) sol.AddEdge(i+n,T,1);
    rep(i,1,n) rep(j,1,m) if(val[i][j]<=x) sol.AddEdge(i,j+n,1);
    return sol.solve(S,T)>=n-k+1;
}
int main() {
    n=read();m=read();k=read();
    int l=inf,r=-inf,mid;
    rep(i,1,n) rep(j,1,m) {
        val[i][j]=read();
        l=min(l,val[i][j]);
        r=max(r,val[i][j]);
    }
    while(l<r) if(check(mid=l+r>>1)) r=mid; else l=mid+1;
    printf("%d\n",l);
    return 0;
}
View Code

 

posted @ 2016-03-23 16:29  wzj_is_a_juruo  阅读(337)  评论(0编辑  收藏  举报