博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

题解 【SCOI2015】小凸玩矩阵

题面

 

解析

这题其实也是网络流建图。。

首先,转换下思路,

求第k大的数的最小值,

其实就是求一个最小的值,

使选取的点中能有(n-k+1)个的值比它小。

因此,可以采用二分答案,

每次判断一个值,

将比它小的点加到图中跑最大流,

看流量是否大于(n-k+1)。

那么,怎么连边呢?

其实,我们可以每一行连源点,流量为1,

每一列连汇点,流量为1,

中间源点与汇点连INF。

最后判断就能AC了!

 

 

上AC代码:

#include<bits/stdc++.h>
using namespace std;

inline int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return f*sum;
}

const int INF=0x3f3f3f3f;
struct node{
    int next,to,v;
}e[1000001];
int n,k,m,ans,s,t;
int a[1001][1001];
int maxn=0;
int head[100001],cnt;
int d[100001],cur[100001];

void add(int x,int y,int v){
    e[++cnt].to=head[x];e[cnt].next=y;
    e[cnt].v=v;head[x]=cnt;
    e[++cnt].to=head[y];e[cnt].next=x;
    e[cnt].v=0;head[y]=cnt;    
}

bool bfs(){    
    memset(d,0,sizeof(d));
    memcpy(cur,head,sizeof(cur));
    queue <int>    que;
    que.push(s);
    d[s]=1;
    while(!que.empty()){
        int x=que.front();
        que.pop();
        for(int i=head[x];i;i=e[i].to){
            int k=e[i].next;
            if(!e[i].v||d[k]) continue;
            d[k]=d[x]+1;
            que.push(k);
        }
    }
    return d[t];
}

int dfs(int x,int mi){
    if(x==t||!mi) return mi;
    int flow=0;
    for(int &i=cur[x];i;i=e[i].to){
        int k=e[i].next;
        if(!e[i].v||d[k]!=d[x]+1) continue;
        int ret=dfs(k,min(mi,e[i].v));
        flow+=ret;mi-=ret;
        e[i].v-=ret;e[i^1].v+=ret;
        if(!mi) break;
    }
    if(mi) d[x]=-1;
    return flow;
}

bool DINIC(int x){
    int ans=0;
    while(bfs()){
        ans+=dfs(s,INF);
    }
    if(ans>=n-k+1) return 1;
    return 0;
}

bool check(int x){
    memset(e,0,sizeof(e));
    memset(head,0,sizeof(head));
    cnt=1;
    for(int i=1;i<=n;i++) add(s,i,1);
    for(int j=1;j<=m;j++) add(j+n,t,1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]<=x) add(i,j+n,INF);
        }
    }
    if(DINIC(x)) return 1;
    return 0;
}

int main(){
    //freopen("matrix.in","r",stdin);
    //freopen("matrix.out","w",stdout);
    n=read();m=read();k=read();
    s=m+n+1;t=s+1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=read();
            maxn=max(maxn,a[i][j]);
        }
    }
    int l=1,r=maxn;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)){
            r=mid-1;ans=mid;
        }
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-03-19 22:09  Hastin  阅读(129)  评论(0编辑  收藏  举报