[SCOI2015]小凸玩矩阵
壹、题目
贰、思考
考虑二分答案,现在要使得我们选出来的数是第 \(k\) 大,就需要选出 \(k-1\) 个大于等于它的数字,并且剩下的数字还得小于等于它,怎么才能满足这俩条件?
其实只需要考虑比它小的数字,因为我们是二分,所以如果我们二分出一个 \(x\),如果无论怎么选都会有大于等于 \(k\) 个小于等于它的,那么我们自然要将 \(x\) 值调小,直到某个时刻最多也只有 \(k\) 个数字小于等于它,这个时候 \(x\) 是一定满足条件的。
叁、代码
using namespace Elaina;
const int maxn=250;
int n,m,k;
int a[maxn+5][maxn+5];
int t[maxn*maxn+5],cnt;
inline void input(){
n=readin(1),m=readin(1),k=readin(1);
rep(i,1,n)rep(j,1,m){
a[i][j]=readin(1);
t[++cnt]=a[i][j];
}
}
struct edge{int to,nxt;}e[maxn*maxn+5];
int tail[maxn+5],ecnt;
inline void add_edge(const int u,const int v){
// printf("add_edge :> %d to %d\n",u,v);
e[++ecnt]=edge{v,tail[u]};tail[u]=ecnt;
}
inline void build(const int x){
// printf("build :> x == %d\n",x);
memset(tail+1,0,n<<2);ecnt=0;
rep(i,1,n)rep(j,1,m)if(a[i][j]<=x)
add_edge(i,j);
}
int vis[maxn+5],match[maxn+5];
int augment(const int u){
for(int i=tail[u],v;i;i=e[i].nxt)if(!vis[v=e[i].to]){
vis[v]=1;
if(!match[v] || augment(match[v])){
match[v]=u;
return 1;
}
}
return 0;
}
inline int hungary(){
int ret=0;
memset(match+1,0,m<<2);
rep(i,1,n){
memset(vis+1,0,m<<2);
if(augment(i))++ret;
}
return ret;
}
inline void bisearch(){
int l=1,r=1e9,mid,ans;
while(l<=r){
mid=(l+r)>>1;
// printf("Now l == %d, r == %d\n",l,r);
build(mid);
if(hungary()>=(n-k+1))ans=mid,r=mid-1;
else l=mid+1;
}
writc(ans,'\n');
}
signed main(){
input();
bisearch();
return 0;
}
用到の小 \(\tt trick\)
二分答案的自适应性,这个需要好好体会。