bzoj 4443: [Scoi2015]小凸玩矩阵
4443: [Scoi2015]小凸玩矩阵
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
ACTY真大神!!!(大家有空去bzoj上搜搜看)
—————以下题解——————
很容易看出是二分答案。。
题目中说是第k大,其实是第n-k+1小(一直看错)。。。
我们每次验证mid时把矩阵中<=mid的数关于坐标连边,跑一遍二分图最大匹配,判断最大匹配数是否大于n-k+1。。
#include<stdio.h> #include<iostream> #include<algorithm> using namespace std; const int N=255; int n,m,k,i,j,ans,a[N][N],f[N],p[N]; int tot,head[N],Next[N*N],to[N*N]; void add(int x,int y) { tot++; to[tot]=y; Next[tot]=head[x]; head[x]=tot; } int dfs(int x,int T) { int i; for(i=head[x];i!=-1;i=Next[i]) if(p[to[i]]!=T) { int y=to[i]; p[y]=T; if(f[y]==0||dfs(f[y],T)) { f[y]=x; return 1; } } return 0; } int erfen(int l,int r) { if(l>r) return l; int mid=(l+r)>>1,i,j,ans=0; tot=0; for(i=1;i<=n;i++) head[i]=-1; for(i=1;i<=m;i++) p[i]=f[i]=0; for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(a[i][j]<=mid) add(i,j); for(i=1;i<=n;i++) ans+=dfs(i,i); if(ans>=n-k+1) return erfen(l,mid-1);else return erfen(mid+1,r); } int main() { scanf("%d%d%d",&n,&m,&k); for(i=1;i<=n;i++) for(j=1;j<=m;j++) { scanf("%d",&a[i][j]); ans=max(ans,a[i][j]); } cout<<erfen(1,ans); return 0; }
一念起,天涯咫尺; 一念灭,咫尺天涯。