[二分][匈牙利算法] Bzoj P4443 小凸玩矩阵
Description
小凸和小方是好朋友,小方给小凸一个N*M(N<=M)的矩阵A,要求小秃从其中选出N个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的N个数中第K大的数字的最小值是多少。
Input
第一行给出三个整数N,M,K
接下来N行,每行M个数字,用来描述这个矩阵
Output
如题
Sample Input
3 4 2
1 5 6 6
8 3 4 3
6 8 6 3
1 5 6 6
8 3 4 3
6 8 6 3
Sample Output
3
HINT
1<=K<=N<=M<=250,1<=矩阵元素<=10^9
题解
- 二分答案转化为判定性问题
- 设二分的答案为mid,用二分图匹配来求最多能选出多少个不大于mid的数即可
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 using namespace std; 5 int n,m,k,cnt,num,l,r,ans,a[260][260],head[100000],vis[260],p[260]; 6 struct edge {int to,from;}e[100000]; 7 void add(int x,int y) { e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; } 8 bool dfs(int x) 9 { 10 for (int i=head[x];i;i=e[i].from) 11 if (!vis[e[i].to]) 12 { 13 vis[e[i].to]=1; 14 if (!p[e[i].to]||dfs(p[e[i].to])) { p[e[i].to]=x; return 1; } 15 } 16 return 0; 17 } 18 bool check(int x) 19 { 20 memset(head,0,sizeof(head)),cnt=num=0; 21 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (a[i][j]<=x) add(i,j); 22 memset(p,0,sizeof(p)); 23 for (int i=1;i<=n;i++) 24 { 25 memset(vis,0,sizeof(vis)); 26 if (dfs(i)) num++; 27 } 28 return num>n-k; 29 } 30 int main() 31 { 32 scanf("%d%d%d",&n,&m,&k); 33 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]),l=min(l,a[i][j]),r=max(r,a[i][j]); 34 while (l<=r) 35 { 36 int mid=l+r>>1; 37 if (check(mid)) ans=mid,r=mid-1; else l=mid+1; 38 } 39 printf("%d",ans); 40 }