P4251 [SCOI2015]小凸玩矩阵
分析:
-
二分答案 + + + 二分图匹配
-
每行每列只能选取 1 1 1 个,将行列连边,建立二分图
要求的是选取的 n n n 个数中第 k k k 大数的数最小值
二分这个去求这个值,每次操作:
-
将 a [ i ] [ j ] < = m i d a[i][j]<=mid a[i][j]<=mid 的连边(建边条件)
-
跑一边二分图最大匹配
若 c n t > n − k + 1 cnt>n-k+1 cnt>n−k+1 则说明当前 m i d > a n s mid>ans mid>ans
最终的答案是二分答案到边界时,恰好有 n − k + 1 n-k+1 n−k+1 个满足 l e n 匹 配 边 < = m i d len_{匹配边}<=mid len匹配边<=mid ,则第 k k k 大的便是 m i d mid mid , 即 a n s ans ans
-
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005;
vector <int> g[N];
int vis[N], pre[N];
bool dfs(int u,int tag)
{
if(vis[u]==tag) return false;
vis[u]=tag;
for(auto v : g[u])
{
if(!pre[v] || dfs(pre[v],tag))
{
pre[v]=u; return true;
}
}
return false;
}
int a[N][N], b[N];
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n,m,k;
cin>>n>>m>>k;
int l=1, r=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
r=max(r,a[i][j]);
}
}
int ans;
while(l<=r)
{
int mid=l+r>>1;
for(int i=1;i<=n;i++)
{
g[i].clear();
for(int j=1;j<=m;j++)
{
if(a[i][j]<=mid) g[i].push_back(j);
}
}
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1;i<=m;i++) pre[i]=0;
int cnt=0;
for(int i=1;i<=n;i++)
{
if(dfs(i,i)) cnt++;
}
if(cnt>=n-k+1) { r=mid-1; ans=mid; }
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}