题目链接:
解题思路:
很明显的二分图:将原点与它能走到的点连一条边,然后做一遍最小点覆盖:即选出最少的军队可以走到所有的城镇:
最小点覆盖 = 总点数 - 最大匹配数:(若不知道的可以看看这篇博客:
(7条消息) 二分图及其多个扩展用法详解 + 模板题:算法竞赛进阶指南 关押罪犯 棋盘覆盖 机器任务 骑士放置 捉迷藏 + 洛谷:P2764 最小路径覆盖问题_wsh1931的博客-CSDN博客)
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 55; int idx[N][N]; char map[N][N]; bool st[N * N]; int n, m, r, c; int match[N * N]; bool graph[N * N][N * N]; bool find(int a)//二分图模板 { for (int b = 1; b <= n * m; b ++ ) { if (st[b]) continue; if (!graph[a][b]) continue; st[b] = true; if (match[b] == -1 || find(match[b])) { match[b] = a; return true; } } return false; } int main() { memset(match, -1, sizeof match); cin >> n >> m >> r >> c; int dx[4] = {r, r, c, c};//枚举能走到的四个方向,注意只能往下走,因此 int dy[4] = {c, -c, r, -r};//dx只能为正数 for (int i = 1, t = 0; i <= n; i ++ )//将二维坐标(i, j)转化为一维坐标t for (int j = 1; j <= m; j ++ ) idx[i][j] = ++ t; for (int i = 1; i <= n; i ++ )//读入 for (int j = 1; j <= m; j ++ ) cin >> map[i][j]; for (int i = 1; i <= n; i ++ )//枚举每个点的四个方向,并向(i, j)所能走到的(a, b)建图 for (int j = 1; j <= m; j ++ ) if (map[i][j] == '.') for (int k = 0; k < 4; k ++ ) { int a = i + dx[k], b = j + dy[k]; if (a < 1 || a > n || b < 1 || b > m || map[a][b] == 'x') continue;//判断边界 graph[idx[i][j]][idx[a][b]] = true;//建图 } int sum = 0, res = 0; for (int i = 1; i <= n; i ++ )//最小点覆盖模板 for (int j = 1; j <= m; j ++ ) if (map[i][j] == '.') { sum ++ ; int a = idx[i][j]; memset(st, false, sizeof st); if (find(a)) res ++ ; } cout << sum - res << endl;//答案即为所有的点 - 最大匹配数 return 0; }