题解 【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; }