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
题解:
看了讨论就发现是二分了,二分ans,判断如果小于ans的有n-k+1个那么ans可以变小。
看了讨论就发现是二分了,二分ans,判断如果小于ans的有n-k+1个那么ans可以变小。
我们可以用网络流来判断,建边如下:
ins(st,i,1),//表示起点向行连一条边
ins(j+n,ed,1),//表示列向终点连一条边
如果i,j这个点的值<=ansins(i,j+n,1)
限制流量为一,就保证了不会选到在同行或者同列的店
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 0x7fffffff/3
using namespace std;
const int N=600;
int n,m,k;
int map[N][N];
int l,r=0,st,ed;
struct node{
int x,y,z,next,other;
}sa[N*N];int len=0,first[N];
void ins(int x,int y,int z)
{
len++;
sa[len].x=x;
sa[len].y=y;
sa[len].z=z;
sa[len].next=first[x];
first[x]=len;
sa[len].other=len+1;
len++;
sa[len].x=y;
sa[len].y=x;
sa[len].z=0;
sa[len].next=first[y];
first[y]=len;
sa[len].other=len-1;
}
int list[100005],h[100005],head,tail;
bool bt()//宽搜只是为了构建h数组
{
memset(h,0,sizeof(h));h[st]=1; //出发点的层次为1
list[1]=st;head=1;tail=2; //宽搜
while(head!=tail)
{
int x=list[head];
for(int k=first[x];k!=-1;k=sa[k].next)
{
int y=sa[k].y;
if( sa[k].z>0 && h[y]==0 )//a[k].c>0让多次构图成为可能
{
// x >y这条边还有流量,并且 y没有访问过
h[y]=h[x]+1; //y作为x的下一层
list[tail++]=y;
}
}
head++;
}
if(h[ed]>0) return true;//如果最后能到ed返回true,否则返回false
else return false;
}
int findans(int x,int f)//函数值等于:带着“期待流量”f从x出发,最后得到的“实际流量”s。
{
if(x==ed) return f; //x如果是终点,那么当前所带的期待目标流量都可以完成
int s=0,t;
for(int k=first[x];k!=-1;k=sa[k].next) // 尝试通过所有孩子结点分散任务
{
int y=sa[k].y;
if( sa[k].z>0 && h[y]==(h[x]+1) && s<f )//a[k].c>0表示x->y这条边还有流量,且y是x的下一层,且“实际流量”<“期望流量”还没有满
{
s+=( t=findans( y , min(sa[k].z,f-s) ) ); //从y出发,而附带的“期望流量”是多少呢?;
sa[k].z-=t;sa[ sa[k].other ].z+=t; //及时修改边的流量,且同时增加反向边的流量
}
}
if(s==0)h[x]=0;//如果x出发没有流量,x从此不可走
return s;
}
bool check(int x)
{
memset(first,-1,sizeof(first));
len=0;
for(int i=1;i<=n;i++)ins(st,i,1);
for(int j=1;j<=m;j++)ins(j+n,ed,1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(map[i][j]<=x)
ins(i,j+n,1);
}
}
int ans=0;
while(bt()!=false)
{
ans+=findans(st,inf);
}
if(ans>=n-k+1) return true;
else return false;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
st=0;ed=n+m+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);r=max(map[i][j],r);
}
}
l=0;
int mid,an;
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)==true) r=mid,an=mid;
else l=mid+1;
}
printf("%d\n",l);
}