4443: [Scoi2015]小凸玩矩阵
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1460 Solved: 685
[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
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小
如果对于一个数x,有一种方案使得选出来<=x的数的个数 >n-k+1
满足单调性,所以可以二分答案
接下来怎么考虑使选出来的方案的满足条件
贪心,<=x的数越多越好
每行每列只能选一个数,经典的二分图匹配模型
可以通过离散来使二分的次数减少
1 #include<bits/stdc++.h> 2 #define N 255 3 using namespace std; 4 int n,m,K,cnt,tot,bl[N*2],vis[N*2],hd[N],a[N][N],b[N*N]; 5 struct edge{int v,next;}e[N*N]; 6 void adde(int u,int v){ 7 e[++tot].v=v; 8 e[tot].next=hd[u]; 9 hd[u]=tot; 10 } 11 12 bool dfs(int u){ 13 //if(vis[u])return 0;vis[u]=1; 14 for(int i=hd[u];i;i=e[i].next){ 15 int v=e[i].v; 16 if(vis[v])continue;vis[v]=1; 17 if(!bl[v]||dfs(bl[v])){ 18 bl[v]=u; 19 return 1; 20 } 21 } 22 return 0; 23 } 24 25 inline bool check(int x){ 26 tot=0;memset(hd,0,sizeof(hd)); 27 memset(bl,0,sizeof(bl)); 28 for(int i=1;i<=n;i++) 29 for(int j=1;j<=m;j++) 30 if(a[i][j]<=x)adde(i,j+n); 31 int ret=0; 32 for(int i=1;i<=n;i++){ 33 memset(vis,0,sizeof(vis)); 34 if(dfs(i))ret++; 35 } 36 return ret>=K; 37 } 38 39 int main(){ 40 scanf("%d%d%d",&n,&m,&K);K=n-K+1; 41 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 42 scanf("%d",&a[i][j]),b[++cnt]=a[i][j]; 43 sort(b+1,b+1+cnt); 44 int len=unique(b+1,b+1+cnt)-b-1; 45 for(int i=1;i<=n;i++) 46 for(int j=1;j<=m;j++) 47 a[i][j]=lower_bound(b+1,b+1+len,a[i][j])-b; 48 int l=1,r=len,mid,ans; 49 while(l<=r){ 50 mid=(l+r)>>1; 51 if(check(mid))r=(ans=mid)-1; 52 else l=mid+1; 53 } 54 printf("%d\n",b[ans]); 55 return 0; 56 }