1. 程式人生 > >2018.10.23【SCOI2015】【洛谷P4251】【BZOJ4443】小凸玩矩陣(二分答案)(二分圖匹配)

2018.10.23【SCOI2015】【洛谷P4251】【BZOJ4443】小凸玩矩陣(二分答案)(二分圖匹配)

洛谷傳送門

解析:

並不知道這道題為什麼在洛谷上是紫題,感覺好水啊。

思路:

把行和列分別當做兩部分圖,中間連邊,可以發現這是一個二分圖匹配。

既然要求kk大值最小,顯然想到二分。那就是第nk+1n-k+1小值最小。

每次二分出一個答案,將所有比它小的邊全部啟用,然後匈牙利找最大匹配,看最後的最大匹配數,如果nk+1\leq n-k+1,則答案過小,下界上移,否則上界下移。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar #define pc putchar #define cs const inline int getint(){ re int num; re char c; while(!isdigit(c=gc()));num=c^48; while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48); return num; } cs int N=251; bool g[N][N]; int a[N][N]; int match[N],vis[N],idx; int n,m,k; inline
int find(int u){ for(int re v=1;v<=m;++v){ if((vis[v]^idx)&&g[u][v]){ vis[v]=idx; if(!match[v]||find(match[v])){ match[v]=u; return true; } } } return false; } inline bool check(int x){ memset(match,0,sizeof match); for(int re i=1;i<=n;++i) for(int re j=1;j<=
m;++j)g[i][j]=a[i][j]<=x; ++idx; int cnt=0; for(int re i=1;i<=n;++i,++idx)if(find(i))++cnt; return cnt>=n-k+1; } int minn=0x3f3f3f3f,maxn=0; signed main(){ n=getint(); m=getint(); k=getint(); for(int re i=1;i<=n;++i) for(int re j=1;j<=m;++j){ a[i][j]=getint(); maxn=max(maxn,a[i][j]); minn=min(minn,a[i][j]); } int l=minn,r=maxn; while(l<r){ int mid=(l+r)>>1; if(check(mid))r=mid; else l=mid+1; } cout<<l; return 0; }