题目:Problem - 590C - Codeforces
用最小的修路格子数,使所有国家都连通。
这道题直接求解比较难,我们可以参考多源BFS:矩阵距离-CSDN博客的求法,正难则反。
根据题意,相同的国家之间一定是连通的。
我们可以求出每个下标到国家1的距离,每个下标到国家2的距离,每个下标到国家3的距离。
- 有可能从这个++国家++开始,连通另外两个国家距离最短。
- 也有可能从这个++道路++开始,连通另外三个国家距离最短。
-
遇到道路,权值为1。遇到国家,权值为0(表示不用修路)。
-
最后遍历所有的点,求出点到所有国家的距离的和,距离最短的就是最优解
-
情况1:遍历的格子是点(道路)

- 由于这个点被统计了3次,减去多余统计的2次:x+y+z-2(各个国家为了连到同一个汇合点,需要占用多少个 '.' 这个点被修了三次,实际上只需要修一次就够了)
-
情况2:遍历的格子是国家

- x+y+z,因为这个格子本身就不需要修路,所以没有"重复支付这个格子的费用的问题"。
cpp
#include <iostream>
#include <deque>
#include <cstring>
using namespace std;
const int N = 1e3+10;
typedef pair<int, int> PII;
int n, m;
char g[N][N];
int dist[4][N][N];
int dx[] = {0, -1, 0, 1};
int dy[] = {1, 0, -1, 0};
void bfs(int num)
{
deque<PII> q;
memset(dist[num], -1, sizeof(dist[num]));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (g[i][j] - '0' == num)
{
dist[num][i][j] = 0;
q.push_back({i, j});
}
}
}
while(q.size())
{
auto t = q.front(); q.pop_front();
int x1 = t.first, y1 = t.second;
for (int i = 0; i < 4; i++)
{
int x2 = x1 + dx[i], y2 = y1 + dy[i];
if (x2 < 1 || y2 < 1 || x2 > n || y2 > m) continue;
if (g[x2][y2] == '#') continue;
int cnt = 0;
if (g[x2][y2] == '.') cnt = 1;
else cnt = 0;
if (dist[num][x2][y2] == -1)
{
dist[num][x2][y2] = dist[num][x1][y1] + cnt;
if (cnt == 1)
{
q.push_back({x2, y2});
}
else
{
q.push_front({x2, y2});
}
}
else
{
if (dist[num][x1][y1] + cnt < dist[num][x2][y2])
{
dist[num][x2][y2] = dist[num][x1][y1] + cnt;
}
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> g[i][j];
}
}
bfs(1);
bfs(2);
bfs(3);
int ret = 1e6+10, cnt = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int a = dist[1][i][j], b = dist[2][i][j], c = dist[3][i][j];
if (g[i][j] == '#' || a == -1 || b == -1 || c == -1 ) continue;
if (g[i][j] == '.')
{
ret = min(ret, a+b+c - 2);
}
else
{
ret = min(ret, a+b+c);
}
}
}
if (ret == 1e6+10) ret = -1;
cout << ret << endl;
return 0;
}