[SCOI2015]小凸玩矩阵

小凸玩矩阵

题解

又一道二分图板子题

其实看到第 k k k大值最小应该很容易想到二分的。
我们可以先二分它的第 k k k大值,判断在当前二分的值下,只选不超过二分值的数是否可得到一种选 n − k + 1 n-k+1 nk+1个数的选法,如果可以,就一定有种方法使得第 k k k大值不超过当前的二分值。
至于判断当前可选的数是否可以选到 n − k + 1 n-k+1 nk+1个,可以通过二分图匹配来进行判断。
我们将行与列分成两组,也就是我们二分图的两边,在每个可选位置的行与列之间连边,跑最大匹配。
这样匹配出来的行和列一定是可选的。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 255
#define reg register
typedef long long LL;
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,k,a[MAXN][MAXN],idx,idy,head[MAXN],tot,cnt,p[MAXN],maxx;
bool vis[MAXN];
struct edge{int to,nxt;}e[MAXN*MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
bool match(int x){
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
		if(p[e[i].to]==-1||(!vis[p[e[i].to]]&&match(p[e[i].to])))
			return (p[e[i].to]=x,1);
	return 0;
}
bool sakura(int mid){
	for(int i=1;i<=n;i++)head[i]=0;tot=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]<=mid)addEdge(i,j);
	for(int i=1;i<=m;i++)p[i]=-1;cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++)vis[j]=0;
		if(match(i))cnt++;
	}
	return cnt>=n-k+1;
}
signed main(){
	read(n);read(m);read(k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			read(a[i][j]),maxx=max(maxx,a[i][j]);
	int l=1,r=maxx,ans=maxx;
	while(l<=r){
		int mid=l+r>>1;
		if(sakura(mid))r=mid-1,ans=mid;
		else l=mid+1;
	} 
	printf("%d",ans);
	return 0;
}

谢谢!!!

posted @ 2021-02-04 15:24  StaroForgin  阅读(14)  评论(0)    收藏  举报  来源