4443: [Scoi2015]小凸玩矩阵
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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;
}