给了一个矩阵 n行m列 选n个数 要保证这n个数不在同行同列,计算出第k大的数最小 , 二分答案,然后我们对于每个a[i][j]<=mid的我们就i和j建立一条边 然后二分求最大匹配必须大于等于n-k-1(因为是第k大 而不是第k小 坑了好久才发现)
#include <algorithm> #include <stdio.h> #include <string.h> #include <vector> #include <iostream> using namespace std; const int maxn=100+5; struct BPM { int n, m; // 左右顶点个数 vector<int> G[maxn]; // 邻接表 int left[maxn]; // left[i]为右边第i个点的匹配点编号,-1表示不存在 bool T[maxn]; // T[i]为右边第i个点是否已标记 int right[maxn]; // 求最小覆盖用 bool S[maxn]; // 求最小覆盖用 void init(int n) { this->n = n; for(int i = 0; i < n; i++) G[i].clear(); } void AddEdge(int u, int v) { G[u].push_back(v); } bool match(int u){ S[u] = true; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!T[v]){ T[v] = true; if (left[v] == -1 || match(left[v])){ left[v] = u; return true; } } } return false; } // 求最大匹配 int solve() { memset(left, -1, sizeof(left)); int ans = 0; for(int u = 0; u < n; u++) { // 从左边结点u开始增广 memset(S, 0, sizeof(S)); memset(T, 0, sizeof(T)); if(match(u)) ans++; } return ans; } }S; int a[maxn][maxn]; void add(int n,int m,int mid) { S.init(n); for(int i=0; i<n; i++) for(int j=0; j<m; j++) if(a[i][j]<=mid) S.AddEdge(i,j); } int main() { int n,m,k; int cas; scanf("%d",&cas); for(int cc=1; cc<=cas; cc++) { scanf("%d%d%d",&n,&m,&k); int L=0,R=0; for(int i=0; i<n; i++) for(int j=0; j<m; j++) { scanf("%d",&a[i][j]); R=max(R,a[i][j]); } int ans=0; while(L<=R) { int mid=(L+R)>>1; add(n,m,mid); int num=S.solve(); if(num>=n-k+1){ ans=mid; R=mid-1; }else{ L=mid+1; } } printf("Case #%d: %d\n",cc,ans); } return 0; }
时间: 2024-10-08 09:45:10