题目描述
小凸和小方是好朋友,小方给了小凸一个 n×mn × mn×m (n≤m)(n≤m)(n≤m) 的矩阵 AAA,并且要求小凸从矩阵中选出 nnn 个数,其中任意两个数都不能在同一行或者同一列。现在小凸想知道,选出的 nnn 个数中第 kkk 大的数的最小值是多少。
输入输出格式
输入格式:
第 111 行读入 333 个整数 n,m,kn,m,kn,m,k。
接下来 nnn 行,每一行有 mmm 个数字,第 iii 行第 jjj 个数字代表矩阵中第 iii 行第 jjj 列的元素 Ai,jA_{i,j}Ai,j。
输出格式:
输出包含一行,为选出的 nnn 个数中第 kkk 大数的最小值。
输入输出样例
输入样例#1:
2 3 1
1 2 4
2 4 1
输出样例#1:
1
输入样例#2:
3 4 2
1 5 6 6
8 3 4 3
6 8 6 3
输出样例#2:
3
说明
对于 2020% 的数据, 1≤n≤m≤91≤n≤m≤91≤n≤m≤9
对于 4040% 的数据, 1≤n≤m≤22,1≤n≤121≤n≤m≤22,1≤n≤121≤n≤m≤22,1≤n≤12
对于 100100% 的数据, 1≤k≤n≤m≤250,1≤Ai,j≤1091≤k≤n≤m≤250,1≤A_{i,j}≤10^91≤k≤n≤m≤250,1≤Ai,j≤109
分析:
选出一些数相当于原图的一个二分图匹配,把横坐标一列点放左边,纵左边一列点放右边,Ai,jA_{i,j}Ai,j就从左边的iii向右边的jjj连边。
我们二分一个答案,然后就是判断这个答案了,具体说就是能否选出至少n−k+1n-k+1n−k+1个小于等于当前midmidmid的数,如果能显然答案可以更小(即使当前midmidmid无法被任何一种选法取到)。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
const int maxn=507;
const int maxe=5e5+7;
const int inf=0x3f3f3f3f;
using namespace std;
int n,m,k,l,r,s,t,cnt,ans;
int a[257][257],dis[maxn],ls[maxn];
struct edge{
int y,w,op,next;
}g[maxe];
queue <int> q;
int po(int x,int y)
{
return (x-1)*m+y;
}
void add(int x,int y,int w)
{
g[++cnt]=(edge){y,w,cnt+1,ls[x]};
ls[x]=cnt;
g[++cnt]=(edge){x,0,cnt-1,ls[y]};
ls[y]=cnt;
}
bool bfs()
{
while (!q.empty()) q.pop();
for (int i=s;i<=t;i++) dis[i]=inf;
dis[s]=0;
q.push(s);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]>dis[x]+1))
{
dis[y]=dis[x]+1;
if (y==t) return true;
q.push(y);
}
}
}
return false;
}
int dfs(int x,int maxf)
{
if ((x==t) || (!maxf)) return maxf;
int ret=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]==dis[x]+1))
{
int f=dfs(y,min(maxf-ret,g[i].w));
if (!f) dis[y]=-1;
g[i].w-=f;
g[g[i].op].w+=f;
ret+=f;
if (ret==maxf) break;
}
}
return ret;
}
bool check(int c)
{
int d=1;
for (int i=1;i<=n;i++)
{
g[d].w=1;
g[d+1].w=0;
d+=2;
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (a[i][j]<=c) g[d].w=1;
else g[d].w=0;
g[d+1].w=0;
d+=2;
}
}
for (int i=1;i<=m;i++)
{
g[d].w=1;
g[d+1].w=0;
d+=2;
}
int flow=0;
while (bfs()) flow+=dfs(s,inf);
return (flow>=(n-k+1));
}
int 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]);
}
s=0,t=n+m+1;
for (int i=1;i<=n;i++) add(s,i,1);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++) add(i,j+n,1);
}
for (int i=1;i<=m;i++) add(i+n,t,1);
l=1,r=1e9;
while (l<=r)
{
int mid=(l+r)/2;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d",ans);
}