1. 程式人生 > >[洛谷 P1263] 宮廷守衛 --- 二分圖最大匹配

[洛谷 P1263] 宮廷守衛 --- 二分圖最大匹配

題目描述

從前有一個王國,這個王國的城堡是一個矩形,被分為M×N個方格。一些方格是牆,而另一些是空地。這個王國的國王在城堡裡設了一些陷阱,每個陷阱佔據一塊空地。

一天,國王決定在城堡里布置守衛,他希望安排儘量多的守衛。守衛們都是經過嚴格訓練的,所以一旦他們發現同行或同列中有人的話,他們立即向那人射擊。因此,國王希望能夠合理地佈置守衛,使他們互相之間不能看見,這樣他們就不可能互相射擊了。守衛們只能被佈置在空地上,不能被佈置在陷阱或牆上,且一塊空地只能佈置一個守衛。如果兩個守衛在同一行或同一列,並且他們之間沒有牆的話,他們就能互相看見。(守衛就像象棋裡的車一樣)

你的任務是寫一個程式,根據給定的城堡,計算最多可佈置多少個守衛,並設計出佈置的方案。

分析

除去牆的存在,這個就是標準的二分圖模型了,橫縱座標連邊即可。 本題同理,只需要處理一下入讀即可,有牆的話,視為換行/列。 方案輸出:匹配的時候順便記錄匹配邊即可。讀入的時候記錄一下邊所代表的點。

程式碼

#include <cstdio>
#include <cstdlib>
#include <cstring>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' >
c || c > '9'; c = getchar()) if(c == '-') k = -1; for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0'; return sum * k; } int n, m; int mp[205][205]; int px[205][205]; struct node { int x, y; IL node(int x_ = 0, int y_ = 0) { x = x_; y = y_;
} }pos[40005]; int to[40005], nxt[40005]; int cnt, last[20005]; IL void add(int u, int v) { to[++cnt] = v; nxt[cnt] = last[u]; last[u] = cnt; } int match[20005]; int mark[20005]; bool vis[20005]; IL bool dfs(int u) { for(int i = last[u], v; (v = to[i]); i = nxt[i]) if(!vis[v]) { vis[v] = 1; if(!match[v] || dfs(match[v])) { match[v] = u; mark[v] = i; return 1; } } return 0; } IL int max_flow() { int sum = 0; for(int i = 1; i <= m; ++i) { memset(vis, 0, sizeof(vis)); if(dfs(i)) ++sum; } return sum; } IL void wri() { for(int i = 1; i <= n; ++i) if(mark[i]) printf("%d %d\n", pos[mark[i]].x, pos[mark[i]].y); } int main() { int l = read(), c = read(); for(int i = 1, flag; i <= l; ++i) { flag = 1; for(int j = 1; j <= c; ++j) { mp[i][j] = read(); if(mp[i][j] == 2) flag = 1; else if(!mp[i][j]) { if(flag) { ++n; flag = 0; } px[i][j] = n; } } } for(int j = 1, flag; j <= c; ++j) { flag = 1; for(int i = 1; i <= l; ++i) { if(mp[i][j] == 2) flag = 1; else if(!mp[i][j]) { if(flag) { ++m; flag = 0; } add(m, px[i][j]); pos[cnt] = node(i, j); } } } printf("%d\n", max_flow()); wri(); return 0; }