二分+二分匹配

给了一个矩阵 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
",cc,ans);
        }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Opaser/p/4782526.html