题目
读题
1. 走同色的格子不花钱
走异色的格子花 1 元
走无色的格子花 2 元
2. 染色很短暂,且不连续
什么意思? 从染色的格子走出后颜色消失。 无法同时走两个格子
思路
很像 BFS / DFS 或者是DP。 我一眼洪水填充
DP
首先考虑DP。
那我们可以设一个状态:
表示走到
行
列的格子。
表示非无色格子(即格子为红色或黄色)
表示无色格子染成红色
表示无色格子染成黄色
接下来考虑状态转移方程
如果是 随便走
如果是 不能走无色格子,现在是红色
如果是 不能走无色格子,现在是黄色
那么则有状态转移方程:
但是这里有问题:
总所周知DP有个特性叫无后效性 Tips:无后效性:已经求解的子问题,不会再受到后续决策的影响
但是如果按照上述的状态转移方程
就会发现会出现后效性。
举例:比如往左走一格,又往右走一格(回去了),再往左走一格。这样就重复了。
所以我们就要使用记忆化搜索
记忆化搜索是一种通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。
但是记忆化搜索 + DP 的代码太过复杂。 作者是蒟蒻写不出来其实是懒
因此我们考虑另外一种思路:
建图
建图,提取图论模型
我们将每一个格子视为一个点.。
我们考虑红黄点如何连边:
如果相邻,那么就连一条边权为0或1的边
如果相差两个以内:连边权为2或3的边
把图建出后,直接跑最短路即可。
写代码
枚举一个格子
看相邻的格子
如果有颜色(黄色或红色)则直接连边权为0或1的边如果
如果无颜色 则再向外拓展一格,碰到有色格子连边权2或3的边
AC代码(保姆注释)
#include <cstring>
#include <iostream>
using namespace std;
const int maxM = 105;
const int maxN = 1005;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
int m, n;
int color[maxM][maxM];
int id[maxM][maxM];
// color[i][j] 表示网格图中 i 行 j 列的格子的颜色
// id[i][j] 表示网格图中 i 行 j 列格子对应的最短路图的编号
struct Mesh
{ int x, y, c; };
Mesh a[maxN];
// 分模块地完成任务
namespace GRAPH
{
// 这里就只处理和图论有关的内容,包括初始化、建图、最短路
// 假设是一个 n 个点的图,边可以添加,要支持查询 1 号点到任意点的最短路。
int dis[maxN]; // 从 1 号点到 i 号点的最短距离
int G[maxN][maxN]; // n 个点的邻接矩阵
void init() // 初始化
{ memset(G, 0x3F, sizeof(G)); }
void link(int u, int v, int w) // 在 u 到 v 之间连长度为 w 的边
{ G[u][v] = G[v][u] = w; }
void BellmanFord(int S)
{
memset(dis, 0x3F, sizeof(dis));
dis[S] = 0;
int flag = 0;
// 最多松弛 n 轮,flag 表示这一轮是否松弛过
for (int k = 1; k <= n; ++k)
{
// 枚举一条边 (i, j)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
{
if (dis[i] > dis[j] + G[i][j])
dis[i] = dis[j] + G[i][j], flag = 1;
}
if (flag == 0)
break;
flag = 0;
}
}
void print()
{
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j) if (G[i][j] < 10)
{
cout << i << ' ' << j << ' ' << G[i][j] << endl;
}
}
}
int main()
{
memset(color, -1, sizeof(color));
cin >> m >> n;
for (int i = 1; i <= n; ++i)
{
cin >> a[i].x >> a[i].y >> a[i].c;
color[a[i].x][a[i].y] = a[i].c;
id[a[i].x][a[i].y] = i;
}
GRAPH::init();
// 枚举一个有色格子 (i, j),尝试从这个有色格子出发找它周围的一些格子连边
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= m; ++j) if (color[i][j] != -1)
{
for (int d = 0; d < 4; ++d)
{
int ii = i + dx[d];
int jj = j + dy[d];
if (ii < 1 or ii > m or jj < 1 or jj > m)
continue;
// 用 u 表示 (i, j) 这一格是第几个点
int u = id[i][j], v;
if (color[ii][jj] != -1)
{
v = id[ii][jj];
GRAPH::link(u, v, color[i][j] != color[ii][jj]);
continue;
}
// 现在,(ii, jj) 是无色格子
for (int k = 0; k < 4; ++k)
{
int iii = ii + dx[k];
int jjj = jj + dy[k];
if (iii < 1 or iii > m or jjj < 1 or jjj > m)
continue;
if (color[iii][jjj] == -1)
continue;
// 现在,(iii, jjj) 是与 (i, j) 相距不超过两格,中间有无色格子连着的有色格子
v = id[iii][jjj];
GRAPH::link(u, v, 2 + int(color[i][j] != color[iii][jjj]));
}
}
}
}
GRAPH::BellmanFord(id[1][1]);
int ans = 1e9;
if (color[m][m] != -1)
ans = min(ans, GRAPH::dis[id[m][m]]);
else
{
if (color[m][m-1] != -1)
ans = min(ans, GRAPH::dis[id[m][m-1]] + 2);
if (color[m-1][m] != -1)
ans = min(ans, GRAPH::dis[id[m-1][m]] + 2);
}
if (ans < int(1e9))
cout << ans << endl;
else
cout << -1 << endl;
return 0;
}