题目描述
小凸和小方是好朋友,小方给了小凸一个 n × m n × m n×m ( n ≤ m ) (n≤m) (n≤m) 的矩阵 A A A,并且要求小凸从矩阵中选出 n n n 个数,其中任意两个数都不能在同一行或者同一列。现在小凸想知道,选出的 n n n 个数中第 k k k 大的数的最小值是多少。
输入输出格式
输入格式:
第
1
1
1 行读入
3
3
3 个整数
n
,
m
,
k
n,m,k
n,m,k。
接下来 n n n 行,每一行有 m m m 个数字,第 i i i 行第 j j j 个数字代表矩阵中第 i i i 行第 j j j 列的元素 A i , j A_{i,j} Ai,j。
输出格式:
输出包含一行,为选出的
n
n
n 个数中第
k
k
k 大数的最小值。
输入输出样例
输入样例#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
≤
9
1≤n≤m≤9
1≤n≤m≤9
对于 4040% 的数据,
1
≤
n
≤
m
≤
22
,
1
≤
n
≤
12
1≤n≤m≤22,1≤n≤12
1≤n≤m≤22,1≤n≤12
对于 100100% 的数据,
1
≤
k
≤
n
≤
m
≤
250
,
1
≤
A
i
,
j
≤
1
0
9
1≤k≤n≤m≤250,1≤A_{i,j}≤10^9
1≤k≤n≤m≤250,1≤Ai,j≤109
分析:
选出一些数相当于原图的一个二分图匹配,把横坐标一列点放左边,纵左边一列点放右边,
A
i
,
j
A_{i,j}
Ai,j就从左边的
i
i
i向右边的
j
j
j连边。
我们二分一个答案,然后就是判断这个答案了,具体说就是能否选出至少
n
−
k
+
1
n-k+1
n−k+1个小于等于当前
m
i
d
mid
mid的数,如果能显然答案可以更小(即使当前
m
i
d
mid
mid无法被任何一种选法取到)。
代码:
// 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);
}