题解:P13174 [GCJ 2017 #2] Shoot the Turrets
这个做法应该比官方题解麻烦点。
忽略所有炮台的限制,求出每个士兵在不超过限制的距离的情况下能与哪些炮台处于同一行或列。在这样的士兵和炮台之间连边得到二分图
考虑怎么构造方案,可以
问题是如何判断每条边是否在某个最大匹配中。首先求出一个最大匹配,然后将最大匹配内的边反向得到图
这样可以做到
可以进一步优化。首先最大匹配不需要每次重新求,注意到删掉两个点之后此时原来的最大匹配大小至多减少
放个
::::info[code]
#include <bits/stdc++.h>
using namespace std;
namespace z {
const int N = 200 + 5;
int R, C, n, m, lim, dis[N][N][N], a[N][N];
pair<int, int> pos[N];
vector<int> p[N], g[N], gr[N];
int vis[N], match[N], used[N];
int cnt[N][N], reachl[N], reachr[N];
vector<int> see[N][N];
bool dfs(int u) {
if(used[u]) return false;
for(int v : p[u]) if(!vis[v] && !used[v]) {
vis[v] = 1;
if(!match[v] || dfs(match[v])) {
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
void bfs(int x, int y, int (&dis)[N][N]) {
queue<array<int, 3>> q;
q.push({x, y, 0});
dis[x][y] = 0;
while(!q.empty()) {
auto [x, y, d] = q.front(); q.pop();
for(int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > R || ny < 1 || ny > C) continue;
if(a[nx][ny] == 1) continue;
if(dis[nx][ny] > d + 1) {
dis[nx][ny] = d + 1;
q.push({nx, ny, d + 1});
}
}
}
}
int low[N], dfn[N], cdfn, stk[N], ins[N], top, id[N], scc;
void tarjan(int u) {
low[u] = dfn[u] = ++cdfn;
stk[++top] = u;
ins[u] = 1;
for(int v : g[u]) {
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(ins[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]) {
int v; ++scc;
do {
v = stk[top--];
ins[v] = 0;
id[v] = scc;
} while(v != u);
}
}
void dfsl(int u) {
reachl[u] = 1;
for(int v : g[u]) if(!reachl[v]) dfsl(v);
}
void dfsr(int u) {
reachr[u] = 1;
for(int v : gr[u]) if(!reachr[v]) dfsr(v);
}
void cal() {
cdfn = scc = 0;
memset(dfn, 0, sizeof(dfn));
for(int i = 1; i <= n + m; i++) if(!dfn[i]) tarjan(i);
set<int> st;
memset(reachl, 0, sizeof(reachl));
memset(reachr, 0, sizeof(reachr));
for(int i = 1; i <= m; i++) st.insert(match[n + i]);
for(int i = 1; i <= n; i++) if(!st.count(i)) dfsl(i);
for(int i = 1; i <= n + m; i++) gr[i].clear();
for(int i = 1; i <= n + m; i++) for(int j : g[i]) gr[j].push_back(i);
for(int i = 1; i <= m; i++) if(!match[n + i]) dfsr(n + i);
}
void solve() {
cin >> C >> R >> lim;
n = m = 0;
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= R; i++) {
string str; cin >> str;
for(int j = 1; j <= C; j++) {
if(str[j - 1] == '.') a[i][j] = 0;
if(str[j - 1] == '#') a[i][j] = 1;
if(str[j - 1] == 'S') a[i][j] = 2;
if(str[j - 1] == 'T') a[i][j] = 3;
see[i][j].clear(); cnt[i][j] = 0;
}
}
for(int i = 1; i <= R; i++)
for(int j = 1; j <= C; j++)
if(a[i][j] == 2) pos[++n] = {i, j};
for(int i = 1; i <= R; i++)
for(int j = 1; j <= C; j++)
if(a[i][j] == 3) {
pos[n + (++m)] = {i, j};
for(int k = i; k >= 1; k--) {
if(a[k][j] == 1) break;
cnt[k][j]++;
see[k][j].push_back(n + m);
}
for(int k = i; k <= R; k++) {
if(a[k][j] == 1) break;
cnt[k][j]++;
see[k][j].push_back(n + m);
}
for(int k = j; k >= 1; k--) {
if(a[i][k] == 1) break;
cnt[i][k]++;
see[i][k].push_back(n + m);
}
for(int k = j; k <= C; k++) {
if(a[i][k] == 1) break;
cnt[i][k]++;
see[i][k].push_back(n + m);
}
cnt[i][j] -= 3;
}
for(int i = 1; i <= n + m; i++) p[i].clear();
for(int i = 1; i <= n; i++) {
bfs(pos[i].first, pos[i].second, dis[i]);
for(int j = 1; j <= m; j++) {
auto [x, y] = pos[n + j];
bool flg = 0;
for(int k = x; k >= 1; k--) {
if(a[k][y] == 1) break;
if(dis[i][k][y] <= lim) flg = 1;
}
for(int k = x; k <= R; k++) {
if(a[k][y] == 1) break;
if(dis[i][k][y] <= lim) flg = 1;
}
for(int k = y; k >= 1; k--) {
if(a[x][k] == 1) break;
if(dis[i][x][k] <= lim) flg = 1;
}
for(int k = y; k <= C; k++) {
if(a[x][k] == 1) break;
if(dis[i][x][k] <= lim) flg = 1;
}
if(flg) p[i].push_back(n + j);
}
}
memset(match, 0, sizeof(match));
memset(used, 0, sizeof(used));
int ans = 0;
for(int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis));
if(dfs(i)) ans++;
}
cout << ans << '\n';
set<pair<int, int>> edge;
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= n; i++) dis[i][pos[i].first][pos[i].second] = 0;
int lst = 0;
while(ans--) {
for(int p = 1; p <= n; p++) {
vector<array<int, 3>> v;
vector<pair<int, int>> newpos;
if(lst) {
int r = pos[lst].first;
for(int c = 1; c <= C; c++)
if(dis[p][r][c] < 1e9) v.push_back({dis[p][r][c], r, c});
int c = pos[lst].second;
for(int r = 1; r <= R; r++)
if(dis[p][r][c] < 1e9) v.push_back({dis[p][r][c], r, c});
} else {
v.push_back({0, pos[p].first, pos[p].second});
newpos.push_back(pos[p]);
}
sort(v.begin(), v.end());
priority_queue<array<int, 3>, vector<array<int, 3>> , greater<array<int, 3>>> q;
for(auto [d, r, c] : v) q.push({d, r, c});
while(!q.empty()) {
auto [d, x, y] = q.top(); q.pop();
if(d + 1 > lim || cnt[x][y]) continue;
for(int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > R || ny < 1 || ny > C) continue;
if(a[nx][ny] == 1 || a[nx][ny] == 3) continue;
if(dis[p][nx][ny] > d + 1) {
dis[p][nx][ny] = d + 1;
newpos.push_back({nx, ny});
q.push({d + 1, nx, ny});
}
}
}
for(auto [x, y] : newpos)
for(int j : see[x][y])
edge.insert({p, j});
}
for(int i = 1; i <= n + m; i++) g[i].clear();
for(int i = 1; i <= n; i++) if(!used[i]) {
for(int j : p[i]) if(!used[j]) {
if(match[j] != i) g[i].push_back(j);
else g[j].push_back(i);
}
}
cal();
for(auto [u, v] : edge) if(!used[u] && !used[v]) {
if(id[u] == id[v] || reachl[u] || reachr[v] || match[v] == u) {
used[u] = used[v] = 1;
match[match[v]] = 0;
match[match[u]] = 0;
match[v] = match[u] = 0;
for(int k = pos[v].first; k >= 1; k--) {
if(a[k][pos[v].second] == 1) break;
cnt[k][pos[v].second]--;
}
for(int k = pos[v].first; k <= R; k++) {
if(a[k][pos[v].second] == 1) break;
cnt[k][pos[v].second]--;
}
for(int k = pos[v].second; k >= 1; k--) {
if(a[pos[v].first][k] == 1) break;
cnt[pos[v].first][k]--;
}
for(int k = pos[v].second; k <= C; k++) {
if(a[pos[v].first][k] == 1) break;
cnt[pos[v].first][k]--;
}
cnt[pos[v].first][pos[v].second] += 3;
a[pos[v].first][pos[v].second] = 0;
lst = v;
cout << u << " " << v - n << '\n';
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++) if(!used[i] && !match[i]) dfs(i);
break;
}
}
}
}
void main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int T; cin >> T;
for(int i = 1; i <= T; i++) {
cout << "Case #" << i << ": ";
solve();
}
}
#undef int
}
int main()
{
z::main();
return 0;
}
::::