4443: [Scoi2015]小凸玩矩阵
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 1762 Solved: 842
[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
HINT
1<=K<=N<=M<=250,1<=矩阵元素<=10^9
按照题目意思应该要二分,所以我们先二分出一个值,在这个矩阵中选出小于这个值的数用行列建边,注意是第k大,所以应该从后面开始,也就是说要选出n-k+1个数,这才是满足二分的条件
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1010;
const int maxm=7e6+7;
int n,m,k;
int map[maxn][maxn];
bool vis[maxn];
int match[maxn];
struct Node
{
int to;
int next;
}edge[maxm];
int cnt;
int head[maxn];
void init()
{
memset(head,-1,sizeof(head));
cnt=0;
return;
}
void add(int u,int v)
{
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt++;
return;
}
bool dfs(int node)
{
for(int i=head[node];~i;i=edge[i].next)
{
int v=edge[i].to;
if(!vis[v])
{
vis[v]=true;
if(match[v]==-1||dfs(match[v]))
{
match[v]=node;
return true;
}
}
}
return false;
}
int hungry()
{
int ans=0;
memset(match,-1,sizeof(match));
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof(vis));
if(dfs(i))
{
ans++;
}
}
return ans;
}
bool judge(int mid)
{
init();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(map[i][j]<=mid)
{
add(i,j+n);
}
}
}
return hungry()>=n-k+1;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&k))
{
memset(map,0,sizeof(map));
int maxx=-1,minn=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
maxx=max(maxx,map[i][j]);
minn=min(minn,map[i][j]);
}
}
int left=minn;
int right=maxx;
int ans=-1;
while(left<=right)
{
int mid=(left+right)>>1;
if(judge(mid))
{
ans=mid;
right=mid-1;
}
else
{
left=mid+1;
}
}
printf("%d\n",ans);
//cout<<ans<<endl;
}
return 0;
}