1. 程式人生 > >bzoj4443[Scoi2015]小凸玩矩陣

bzoj4443[Scoi2015]小凸玩矩陣

第k大 com style bit mem pan 離散 模型 減少

4443: [Scoi2015]小凸玩矩陣

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 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

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 }

bzoj4443[Scoi2015]小凸玩矩陣