您的位置:首页 > 其它

bzoj 4443: [Scoi2015]小凸玩矩阵(二分+二分匹配)

2017-07-15 01:20 387 查看

4443: [Scoi2015]小凸玩矩阵

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1002  Solved: 505

[Submit][Status][Discuss]

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

Sample Output

3

题目中说求第k大,相当于就是求第n-k+1小

那如何判断某个值p是否可以是选出来的N个数中第n-k+1个小的呢?

将棋盘转成二分图,所有的行作为二分图的左半部分,所有的列作为二分图的右半部分,如果图中点(x, y)上值<=p,那么x和y连一条边,如果最大匹配>=n-k+1,说明p可以作为第k大的数字

之后二分这个p值即可

#include<stdio.h>
#include<string.h>
int n, m, k, vis[255], link[255], a[255][255], road[255][255];
int Sech(int x)
{
int i;
for(i=1;i<=m;i++)
{
if(vis[i]==0 && road[x][i])
{
vis[i] = 1;
if(link[i]==0 || Sech(link[i]))
{
link[i] = x;
return 1;
}
}
}
return 0;
}
int Jud(int x)
{
int i, j, sum;
memset(road, 0, sizeof(road));
memset(link, 0, sizeof(link));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(a[i][j]<=x)
road[i][j] = 1;
}
}
sum = 0;
for(i=1;i<=n;i++)
{
memset(vis, 0, sizeof(vis));
if(Sech(i))
sum++;
}
if(sum>=k)
return 1;
return 0;
}
int main(void)
{
int i, j, l, r, mid;
while(scanf("%d%d%d", &n, &m, &k)!=EOF)
{
k = n-k+1;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
scanf("%d", &a[i][j]);
}
l = 1, r = 1000000000;
while(l<r)
{
mid = (l+r)/2;
if(Jud(mid))
r = mid;
else
l = mid+1;
}
printf("%d\n", l);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: