小凸玩矩阵

简单题,我秒了。
(k=n*m-k),等于询问(k)小值。
考虑二分,二分答案(md),考虑判定是否存在一种方案使得(k)小值(leq md)
事实上就是判定是否存在一个选数方案使得(leq md)的数数量(geq k)
题目中一行一列最多只能选择一个数,用(1)流量限制。
考虑二分图。把每一行/列拆成s->行,列->t的两条流量为(1)的边。
用一条(s->t)(1)流表示选择一个格子,对于一个格子((i,j))如果(leq md),则左边(i)到右边(j)连一条边。

#include<bits/stdc++.h>
using namespace std;
#define N 200010
#define M 200010
int h[M],nxt[M],v[M],w[M],s,t,dep[M],ec,n,m,k,p[N],a[500][500];
void add(int a,int b,int c){v[++ec]=b;w[ec]=c;nxt[ec]=h[a];h[a]=ec;}
void adj(int a,int b,int c){
	add(a,b,c);
	add(b,a,0);
}
bool bfs(){
    queue<int>q;
	q.push(s);
    for(int i=0;i<=t;i++)
    	dep[i]=0;
	dep[s]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=h[x];i;i=nxt[i])
            if(w[i]&&!dep[v[i]]){
            	dep[v[i]]=dep[x]+1;
				q.push(v[i]);
            	if(v[i]==t)
					return 1;
			}
    }
	return 0;
}
int dfs(int x,int dis){
    if(x==t)
		return dis;
	int tp=dis;
    for(int i=h[x];i;i=nxt[i])
        if(dep[v[i]]==dep[x]+1&&w[i]){
            int f=dfs(v[i],min(tp,w[i]));
            if(!f)
				dep[v[i]]=0;
            tp-=f;
            w[i]-=f;
			w[i^1]+=f;
            if(!tp)
				break;
        }
    return dis-tp;
}
int din(){
    int aans=0;
    while(bfs()){
    	int v;
    	while(v=dfs(s,1e9))
			aans+=v;
	}
    return aans;
}
int pd(int md){
	ec=1;
	memset(h,0,sizeof(h));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]<=md)
				adj(i,j+n,1);
	t=n+1+m;
	for(int i=1;i<=n;i++)
		adj(s,i,1);
	for(int i=1;i<=m;i++)
		adj(i+n,t,1);
	return din()>=n-k+1;
}
signed main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
	int l=1,r=1e9,ans=1;
	while(l<=r){
		int md=(l+r)/2;
		if(pd(md)){
			r=md-1;
			ans=md;
		}
		else
			l=md+1;
	}
	printf("%d",ans);
}

原文地址:https://www.cnblogs.com/ctmlpfs/p/14951890.html