小凸玩矩阵
简单题,我秒了。
把(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);
}