KM算法模版(LRJ)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 500 + 10;
const int INF = 1000000000;

int W[maxn][maxn], n;
int Lx[maxn], Ly[maxn]; // 顶标
int left[maxn];         // left[i]为右边第i个点的匹配点编号
bool S[maxn], T[maxn];   // S[i]和T[i]为左/右第i个点是否已标记

bool match(int i){
  S[i] = true;
  for(int j = 1; j <= n; j++) if (Lx[i]+Ly[j] == W[i][j] && !T[j]){
    T[j] = true;
    if (!left[j] || match(left[j])){
      left[j] = i;
      return true;
    }
  }
  return false;
}

void update(){
  int a = INF;
  for(int i = 1; i <= n; i++) if(S[i])
    for(int j = 1; j <= n; j++) if(!T[j])
      a = min(a, Lx[i]+Ly[j] - W[i][j]);
  for(int i = 1; i <= n; i++) {
    if(S[i]) Lx[i] -= a;
    if(T[i]) Ly[i] += a;
  }
}

void KM() {
  for(int i = 1; i <= n; i++) {
    left[i] = Lx[i] = Ly[i] = 0;
    for(int j = 1; j <= n; j++)
      Lx[i] = max(Lx[i], W[i][j]);
  }
  for(int i = 1; i <= n; i++) {
    for(;;) {
      for(int j = 1; j <= n; j++) S[j] = T[j] = false;
      if(match(i)) break; else update();
    }
  }
}


上面模版是对完全图;

下面的是对需要构边的图,用邻接表存

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

const int maxn = 500 + 5; // 顶点的最大数目
const int INF = 1000000000;

// 最大权匹配
struct KM {
  int n;                  // 左右顶点个数
  vector<int> G[maxn];    // 邻接表
  int W[maxn][maxn];      // 权值
  int Lx[maxn], Ly[maxn]; // 顶标
  int left[maxn];         // left[i]为右边第i个点的匹配点编号,-1表示不存在
  bool S[maxn], T[maxn];  // S[i]和T[i]为左/右第i个点是否已标记

  void init(int n) {
    this->n = n;
    for(int i = 0; i < n; i++) G[i].clear();
    memset(W, 0, sizeof(W));
  }

  void AddEdge(int u, int v, int w) {
    G[u].push_back(v);
    W[u][v] = w;
  }

  bool match(int u){
    S[u] = true;
    for(int i = 0; i < G[u].size(); i++) {
      int v = G[u][i];
      if (Lx[u]+Ly[v] == W[u][v] && !T[v]){
        T[v] = true;
        if (left[v] == -1 || match(left[v])){
          left[v] = u;
          return true;
        }
      }
    }
    return false;
  }

  void update(){
    int a = INF;
    for(int u = 0; u < n; u++) if(S[u])
      for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if(!T[v]) a = min(a, Lx[u]+Ly[v] - W[u][v]);
      }
    for(int i = 0; i < n; i++) {
      if(S[i]) Lx[i] -= a;
      if(T[i]) Ly[i] += a;
    }
  }

  void solve() {    
    for(int i = 0; i < n; i++) {
      Lx[i] = *max_element(W[i], W[i]+n);
      left[i] = -1;
      Ly[i] = 0;
    }
    for(int u = 0; u < n; u++) {
      for(;;) {
        for(int i = 0; i < n; i++) S[i] = T[i] = false;
        if(match(u)) break; else update();
      }
    }
  }
};

KM solver;


 O(n4)的复杂度,使用时没有理论上那么糟,编号得从1开始,若最优匹配的前提最大匹配不满足,会死循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值