2021寒假图论复习计划(4)(hduoj3020)

Antenna Placement(二分图匹配)

oj: hduoj 3020
VJudge

题意

有一个 n × m n\times m n×m 的网格,网格内有若干个城市,每个基站可以覆盖两个相邻得城市。请你使用尽可能少的基站覆盖所有的城市。

题解

先把相邻的城市之间连边建立二分图,然后求最小路径覆盖。

先给出一些概念:

  • 匹配:给定一个二分图,在 G G G 的一个子图 G ′ G' G 中,如果 G ′ G' G 的边集中的任意两条边都不依附于同一个顶点,则称 G ′ G' G 的边集为 G G G 的一个匹配。
  • 最大匹配:在所有的匹配中,边数最多的那个匹配就是二分图的最大匹配了。
  • 顶点覆盖:在顶点集合中,选取一部分顶点,这些顶点能够把所有的边都覆盖了。这些点就是顶点覆盖集。
  • 最小顶点覆盖:在所有的顶点覆盖集中,顶点数最小的那个叫最小顶点集合。
  • 独立集:在所有的顶点中选取一些顶点,这些顶点两两之间没有连线,这些点就叫独立集。
  • 最大独立集:在所有的独立集中,顶点数最多的那个集合。
  • 路径覆盖:在图中找一些路径,这些路径覆盖图中所有的顶点,每个顶点都只与一条路径相关联。
  • 最小路径覆盖:在所有的路径覆盖中,路径个数最小的就是最小路径覆盖了。

下面给出一些图的性质:

  • 最大团 = 补图的最大独立集
  • 最小边覆盖 = 二分图最大独立集 = ∣ V ∣ |V| V - 最小路径覆盖
  • 最小路径覆盖 = ∣ V ∣ |V| V - 最大匹配数
  • 最小顶点覆盖 = 最大匹配数
  • 最小顶点覆盖 + 最大独立数 = ∣ V ∣ |V| V
  • 最小割 = 最小点权覆盖集 = 点权和 - 最大点权独立集

以上概念及性质参考自kitten.one的博客

代码

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <stack>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
using namespace std;
typedef long long LL;
const int maxn = 1605;
const int inf = 0x3f3f3f3f;

inline int read() {
    
    
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') {
    
     if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n, m;
char s[45][15];

/*
二分图匹配-HK算法
初始化:清空邻接表
参数:
Maxmatch:返回最大匹配数
ln:左边点集的大小
注意:点的编号从0开始
*/
vector<int> G[maxn];
int Mx[maxn], My[maxn]; //Mx存与左侧集合相连接的右边的点,My存与右边的
int dx[maxn], dy[maxn]; //层次编号
int dis;                //标识是否找到增广路及找到的增广路的深度
int used[maxn];         //dfs中标识是否走过
bool bfs(int ln) {
    
    
    queue<int> q;
    dis = inf;
    memset(dx, -1, sizeof(dx));
    memset(dy, -1, sizeof(dy));
    _for(i, ln) {
    
    
        if (Mx[i] == -1) {
    
          //如果没有匹配过
            q.push(i);
            dx[i] = 0;
        }
    }
    while (q.size()) {
    
    
        int u = q.front();
        q.pop();
        if (dx[u] > dis) break; //如果已经找到增广路
        int sz = G[u].size();
        _for(i, sz) {
    
    
            int v = G[u][i];
            if (dy[v] == -1) {
    
    
                dy[v] = dx[u] + 1;
                if (My[v] == -1) dis = dy[v];
                else {
    
    
                    dx[My[v]] = dy[v] + 1;
                    q.push(My[v]);
                }
            }
        }
    }
    return dis != inf;
}//找出增广路并保存路径
bool dfs(int u) {
    
    
    int sz = G[u].size();
    _for(i, sz) {
    
    
        int v = G[u][i];
        if (!used[v] && dy[v] == dx[u] + 1) {
    
    
            used[v] = 1;
            if (My[v] != -1 && dy[v] == dis) continue;
            if (My[v] == -1 || dfs(My[v])) {
    
    
                My[v] = u;
                Mx[u] = v;
                return 1;
            }
        }
    }
    return 0;
}
int Maxmatch(int ln) {
    
    
    int ans = 0;
    memset(Mx, -1, sizeof(Mx));
    memset(My, -1, sizeof(My));
    while (bfs(ln)) {
    
    
        memset(used, 0, sizeof(used));
        _for(i, ln) {
    
    
            if (Mx[i] == -1 && dfs(i)) {
    
    
                ans++;
            }
        }
    }
    return ans;
}

void init() {
    
    
    _for(i, n * m) G[i].clear();
}

void sol() {
    
    
    init();
    _for(i, n) scanf("%s", s[i]);
    int d[4][2] = {
    
    0, 1, 1, 0, 0, -1, -1, 0};
    int cnt = 0;
    _for(i, n) _for(j, m) if(s[i][j] == '*') {
    
    
        ++cnt;
        _for(k, 4) {
    
    
            int x = i + d[k][0], y = j + d[k][1];
            if(x >= 0 && x < n && y >= 0 && y < m && s[x][y] == '*') {
    
    
                G[i * m + j].push_back(x * m + y);
            }
        }
    }
    printf("%d\n", cnt - Maxmatch(n * m) / 2);
}

int main() {
    
    
    int T = read();
    _for(i, T) {
    
    
        n = read(), m = read();
        sol();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/113005828
今日推荐