题目大意
题目说的很清楚,不说了。
解题分析
学过二分图匹配的一定做过一道题,要求每行每列只能选一个问最多能拿多少个,这个题就是行向列连边然后二分图匹配就行。那么这道题是多了一个K大值最小,也可以看做n-K+1大值最小,那么最大最小就是二分答案,那么验证,就需要保证所有低于mid的是合法可以连边,如果最后可以选出n-K+1个数小于mid,那么因为n<=m,所以剩下来的可以任意选都合法,也就是可以通过。
复杂度:
时间:O(
n2∗log(max(ai))
);
空间:O(
nm
);
#include<cstdio>
#include<cstring>
#define maxn 255
#define maxe 80005
using namespace std;
int n,m,tot,L,R,k,mp[maxn][maxn],lnk[maxn],son[maxe],nxt[maxe],who[maxn];
bool vs[maxn];
inline void readi(int &x){
x=0; char ch=getchar();
while ('0'>ch||ch>'9') ch=getchar();
while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
}
void _add(int x,int y){
tot++; son[tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;
}
bool _find(int x){
if (vs[x]) return false; vs[x]=true;
for (int j=lnk[x];j;j=nxt[j])
if (!who[son[j]]||_find(who[son[j]])){
who[son[j]]=x;
return true;
}
return false;
}
bool _check(int x){
tot=0; int tem=0;
memset(lnk,0,sizeof(lnk));
memset(who,0,sizeof(who));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (x>=mp[i][j]) _add(i,j);
for (int i=1;i<=n;i++){
memset(vs,0,sizeof(vs));
tem+=_find(i);
}
return tem>=k;
}
int main()
{
freopen("square.in","r",stdin);
freopen("square.out","w",stdout);
readi(n); readi(m); readi(k); k=n-k+1; L=1e9+1; R=-1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
readi(mp[i][j]);
if (L>mp[i][j]) L=mp[i][j];
if (R<mp[i][j]) R=mp[i][j];
}
while (L<=R){
int mid=(R-L)/2+L;
if (_check(mid)) R=mid-1;
else L=mid+1;
}
printf("%d\n",L);
return 0;
}