首先,二分图的问题都可以用网络流的相关知识解决,但是匈牙利算法也有不错的效果。
二分图相关概念:
(1)最大匹配数:用匈牙利算法可求得。
(2)最小点覆盖=最大匹配
(3)最小边覆盖=最大独立集=总节点数-最大匹配
1.【线性规划与网络流24题#24】骑士共存
solution:二分图板题,即求最大独立集,除了障碍其他的点与自己能够到达的点连边,跑最大匹配即可,最后用总格子数减去障碍数再减去最大匹配的一半即可。
solution:暂时鸽了,贴个码,考察算法:二分图 + 最短路 + 二分。
#include <bits/stdc++.h> using namespace std; int dx[4] = {0, -1, 0, 1}; int dy[4] = {-1, 0, 1, 0}; typedef pair <int, int> P; struct battle { int x, y; }st[100005], des[2000005]; int m, n, k, t, ct, cnt, vis[505][505], cost[505][505], lk[500005], vvis[500005], dis[505][505], h[505][505]; bool check(int x, int y) {return x >= 1 && x <= m && y >= 1 && y <= n;} bool spfa(int s, int p) { queue <P> Q; memset(dis, 0x3f, sizeof dis), memset(vis, 0, sizeof vis); dis[st[s].x][st[s].y] = 0; Q.push(make_pair(st[s].x, st[s].y)); while (!Q.empty()) { int x = Q.front().first, y = Q.front().second; Q.pop(); vis[x][y] = 0; for (int i = 0; i < 4; i++) { int fx = x + dx[i], fy = y + dy[i], l = (p ^ (dis[x][y] & 1) ? h[x][y] < h[fx][fy] : h[x][y] > h[fx][fy]); if (check(fx, fy)) { if (dis[fx][fy] > dis[x][y] + l) { dis[fx][fy] = dis[x][y] + l; if (!vis[fx][fy]) vis[fx][fy] = 1, Q.push(make_pair(fx, fy)); } } } } for (int i = 1; i <= (k << 1 | 1); i++) cost[s][i] = dis[des[i].x][des[i].y]; } bool find(int u, int lim) { for (int v = 1; v <= 2 * k + 1; v++) { if ((vvis[v] ^ cnt) && cost[u][v] <= lim) { vvis[v] = cnt; if (!lk[v] || find(lk[v], lim)) return lk[v] = u, 1; } } return 0; } bool check(int lim) { memset(vvis, 0, sizeof vvis), cnt = 0; memset(lk, 0, sizeof lk); int ans = 0; for (int i = 1; i <= (k << 1); i++) ++cnt, ans += find(i, lim); return ans + lim >= 2 * k; } int main() { scanf("%d%d%d%d", &m, &n, &k, &t); for (int i = 1; i <= (k << 1 | 1); i++) scanf("%d%d", &st[i].x, &st[i].y); for (int i = 1, z; i <= t; i++) { scanf("%d%d%d", &des[i].x, &des[i].y, &z); --z; while (z--) ++ct, des[t + ct] = des[i]; } for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) scanf("%d", &h[i][j]); for (int i = 1; i <= (k << 1); i++) { spfa(i, i > k); } int l = 0, r = 2 * k, mid, ans = 0; while (l <= r) { if (check(mid = l + r >> 1)) r = mid - 1, ans = mid; else l = mid + 1; } printf("%d", ans); }
3.[HNOI2013 DAY1]消毒
solution:暴力枚举最小一维的状态,将其强行转化为二维,于是转化为行列问题,可以用二分图解决。
#include <bits/stdc++.h> using namespace std; int x[500005], y[500005], z[500005], vis[500005], cnt; int a, b, c, ok[500005]; struct node { int to, nxt; }e[2000005]; int head[1000005], tot, lk[500005]; inline void add_e(int u, int v) {e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot;} bool find(int u) {for (int i = head[u], v; i; i = e[i].nxt) if ((v = e[i].to) && (vis[v] ^ cnt) && (vis[v] = cnt) && (!lk[v] || find(lk[v])) && (lk[v] = u)) return 1; return 0;} void solve() { int minn = 0x3f3f3f3f, ans = 0x3f3f3f3f, ct = 0; scanf("%d%d%d", &a, &b, &c); minn = min(a, min(b, c)); for (int i = 1; i <= a; i++) for (int j = 1; j <= b; j++) for (int k = 1, mm; k <= c; k++) { scanf("%d", &mm); if (!mm) continue; x[++ct] = i, y[ct] = j, z[ct] = k; } if (minn == b) swap(a, b), swap(x, y); if (minn == c) swap(a, c), swap(x, z); int all = (1 << a) - 1; for (int s = 0; s <= all; s++) { tot = 0, cnt = 0; int cost = 0; for (int i = 1; i <= b; i++) head[i] = vis[i] = lk[i] = 0; for (int i = 1; i <= a; i++) { if ((1 << (i - 1)) & s) ok[i] = 0, ++cost; else ok[i] = 1; } for (int i = 1; i <= ct; i++) { if (ok[x[i]]) add_e(y[i], z[i]); } for (int i = 1; i <= b; i++) ++cnt, cost += find(i); ans = min(ans, cost); } printf("%d\n", ans); } int main() { int t; scanf("%d", &t); while (t--) solve(); return 0; }