bzoj 4443: [Scoi2015]小凸玩矩阵
Posted CXCXCXC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 4443: [Scoi2015]小凸玩矩阵相关的知识,希望对你有一定的参考价值。
Submit: 149 Solved: 81
[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
题解:
N个数中的第K大,就是第N-K+1小,这点要是没看见就毁了。设这个数为x,二分这个数,判断是否合法。
判断合法的方法,N^2枚举矩阵中的每个数,如果这个a[i][j]数小于等于x,让i连一条到j,容量为1的边。因为要保证N个数的i,j各不相同,所以设行i为x集,列j为y集,所有边权均为1,做最大流,也就是二分图的最大匹配。如果跑出来的maxflow大于等于N-K+1,说明x满足条件。注意以上两个不等关系都是大于等于,因为要考虑这样一种情况,整个矩阵的数字都是1,第1小是1,第N小还是1。我被这个坑了好久。。。
1 /************************************************************** 2 Problem: 4443 3 User: __abcdef__ 4 Language: C++ 5 Result: Accepted 6 Time:216 ms 7 Memory:14640 kb 8 ****************************************************************/ 9 10 #include<iostream> 11 #include<cstdio> 12 #include<cstdlib> 13 #include<cstring> 14 #include<cmath> 15 #include<algorithm> 16 #include<queue> 17 #include<vector> 18 using namespace std; 19 typedef long long LL; 20 const int inf=1e9,maxn=300; 21 int N,M,K,S,T,tot,MIN=inf,MAX; 22 int a[maxn][maxn],tmp[maxn][maxn]; 23 struct MAT{ 24 int x,y,v,f; 25 }mat[maxn*maxn]; 26 inline int cmp(const MAT & e,const MAT & w){ 27 return e.v<w.v; 28 } 29 30 struct Edge{ 31 int to,rest,next; 32 }e[maxn*maxn*10]; 33 int head[maxn*maxn],cnt=1; 34 inline void Addedge(int x,int y,int r){ 35 e[++cnt].to=y; e[cnt].rest=r; e[cnt].next=head[x]; head[x]=cnt; 36 e[++cnt].to=x; e[cnt].rest=0; e[cnt].next=head[y]; head[y]=cnt; 37 } 38 39 int dis[maxn*maxn]; 40 inline bool BFS(){ 41 memset(dis,0,sizeof(dis)); 42 static queue<int> Q; 43 while(!Q.empty()) Q.pop(); 44 Q.push(S); dis[S]=1; 45 while(!Q.empty()){ 46 int x=Q.front(); Q.pop(); 47 for(int i=head[x];i;i=e[i].next){ 48 int y=e[i].to; 49 if(dis[y]==0&&e[i].rest){ 50 dis[y]=dis[x]+1; 51 Q.push(y); 52 } 53 } 54 } 55 if(dis[T]!=0) return true; 56 return false; 57 } 58 inline int DFS(int x,int flow){ 59 if(x==T) return flow; 60 int now=0,temp; 61 for(int i=head[x];i;i=e[i].next){ 62 int y=e[i].to; 63 if(dis[y]==dis[x]+1&&e[i].rest){ 64 temp=DFS(y,min(flow-now,e[i].rest)); 65 e[i].rest-=temp; 66 e[i^1].rest+=temp; 67 now+=temp; 68 if(now==flow) return flow; 69 } 70 } 71 if(!now) dis[x]=0; 72 return now; 73 } 74 75 inline int dinic(){ 76 int ans=0; 77 while(BFS()==true) ans+=DFS(S,inf); 78 return ans; 79 } 80 81 inline bool jud(int x){ 82 83 for(int i=1;i<=cnt+10;i++) e[i].to=e[i].rest=e[i].next=0; 84 memset(head,0,sizeof(head)); cnt=1; 85 S=0; T=N+M+1; 86 for(int i=1;i<=N;i++) Addedge(S,i,1);// S 连向 x集 权值为 1 87 for(int i=1;i<=M;i++) Addedge(N+i,T,1);//y集 连向 T 权值为 1 88 for(int i=1;i<=N;i++){// x集 连向 y集 89 for(int j=1;j<=M;j++){ 90 if(tmp[i][j]<=x){ 91 Addedge(i,N+j,1); 92 } 93 } 94 } 95 int maxflow=dinic(); 96 if(maxflow>=N-K+1) return true; 97 else return false; 98 } 99 inline int find(int l,int r){ 100 if(l+1>=r){ 101 if(jud(l)==true) return l; 102 else return r; 103 } 104 int mid=(l+r)>>1; 105 if(jud(mid)==true) return find(l,mid); 106 else return find(mid+1,r); 107 } 108 int main(){ 109 scanf("%d%d%d",&N,&M,&K); 110 for(int i=1;i<=N;i++){ 111 for(int j=1;j<=M;j++){ 112 scanf("%d",&a[i][j]); 113 mat[++tot].x=i; mat[tot].y=j; mat[tot].v=a[i][j]; 114 } 115 } 116 tot=0; 117 sort(mat+1,mat+N*M+1,cmp); 118 for(int i=1;i<=N*M;i++){ 119 if(mat[i].v!=mat[i-1].v) mat[i].f=++tot; 120 else mat[i].f=tot; 121 } 122 for(int i=1;i<=N*M;i++){ 123 tmp[mat[i].x][mat[i].y]=mat[i].f; 124 MIN=min(MIN,mat[i].f); MAX=max(MAX,mat[i].f); 125 } 126 int ggg=find(MIN,MAX); 127 for(int i=1;i<=N;i++){ 128 for(int j=1;j<=M;j++){ 129 if(tmp[i][j]==ggg){ 130 printf("%d\n",a[i][j]); 131 return 0; 132 } 133 } 134 } 135 return 0; 136 }
以上是关于bzoj 4443: [Scoi2015]小凸玩矩阵的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ4443[Scoi2015]小凸玩矩阵 二分+二分图最大匹配
bzoj4443[Scoi2015]小凸玩矩阵 二分+二分图匹配