制作不易,点个赞吧!来源:http://115.29.212.188/problem/CCF-1142
通用的BFS模板+记忆化
记忆点在于cost[MAX_SIZE][MAX_SIZE][3], 3代表每个点不同颜色的最小值,仅仅是当这个点是无色的时候有效;
记忆化搜索其实没有通用的记忆点解决套路,通常的BFS只要记录1或者0,表示是否走过
需要根据题意找到每个点的唯一状态去记忆
就是要找到每个点的某种状态,我们肯定要使用最优,这题的状态点是color颜色
题目:
NOIP201703棋盘
时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:普及+/提高
分数:100 OI排行榜得分:16(0.1*分数+2*难度)
出题人:root
描述
有一个 m × m 的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。
任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的),你只能向上、下、左、右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1 个金币。另外,你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。
但这个魔法不能连续使用,而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法;只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?
输入描述
第一行包含两个正整数 m,n,以一个空格分开,分别代表棋盘的大小和棋盘上有颜色的格子的数量。接下来的 n 行,每行三个正整数 x,y,c,分别表示坐标为(x,y)的格子有颜色 c。其中 c=1 代表黄色,c=0 代表红色。相邻两个数之间用一个空格隔开。棋盘左上角的坐标为(1, 1),右下角的坐标为(m, m)。棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(1,1)一定是有颜色的。
输出描述
一行,一个整数,表示花费的金币的最小值,如果无法到达,输出-1。
用例输入 1
5 7 1 1 0 1 2 0 2 2 1 3 3 1 3 4 0 4 4 1 5 5 0
用例输出 1
8
提示
【输入输出样例说明】
从(1,1)开始,走到(1,2)不花费金币,
从(1,2)向下走到(2,2)花费 1 枚金币,
从(2,2)施展魔法,将(2,3)变为黄色,花费 2 枚金币,
从(2,2)走到(2,3)不花费金币,
从(2,3)走到(3,3)不花费金币,
从(3,3)走到(3,4)花费 1 枚金币,
从(3,4)走到(4,4)花费 1 枚金币,
从(4,4)施展魔法,将(4,5)变为黄色,花费 2 枚金币,
从(4,4)走到(4,5)不花费金币,
从(4,5)走到(5,5)花费 1 枚金币,共花费 8 枚金币。
【数据规模与约定】
对30%的数据,1 ≤ m ≤ 5, 1 ≤ n ≤ 10。
对60%的数据,1 ≤ m ≤ 20, 1 ≤ n ≤ 200。
对100%的数据,1 ≤ m ≤ 100, 1 ≤ n ≤ 1,000。
答案来咯——————
#include <bits/stdc++.h>
using namespace std;
//每个点需要记录cost(花费)和color(颜色),step不怎么需要,只是通用模板一般都记录步长
typedef struct Node{
int x,y;
int step;
int color;
int cost;
}Node;
const int MAX_SIZE = 128;
int mp[MAX_SIZE][MAX_SIZE]; //每个点输入的颜色记录
int cost[MAX_SIZE][MAX_SIZE][3]; //记忆点,通常的BFS只要记录1或者0,表示是否走过,本题是要记录cost(花费)
int direct_x[4] = {1, -1, 0, 0}; //四方向
int direct_y[4] = {0, 0, -1, 1}; //四方向
int main()
{
int m, n;
cin >> m >> n;
for (int i = 0; i < n; i++)
{
int a, b, c;
cin >> a >> b >> c;
mp[a][b] = c+1;
}
memset(cost, 0, sizeof(cost));
//起点
Node st = {1, 1, 0, mp[1][1], 1};
Node ed = {m, m, 0, mp[m][m], 0};
queue<Node> q;
//q.push(起点)
q.push(st);
cost[1][1][mp[1][1]] = 1;
int ans = INT_MAX;
while(!q.empty())
{
//获得队列顶部元素,并删除
Node top = q.front();
q.pop();
//如果是重点,记录一下最优值
if (top.x == ed.x && top.y == ed.y)
{
if (ans > top.cost)
ans = top.cost;
continue;
}
Node nxt;
//点top -> 点nxt(这里按照题目进行4方向扩散)
for (int i = 0; i < 4; i++)
{
//Node nxt = top+方向;
nxt.x = top.x + direct_x[i];
nxt.y = top.y + direct_y[i];
//nxt在地图范围内
if (nxt.x < 1 || nxt.y < 1 ||
nxt.y > m || nxt.y > m)
{
continue;
}
//3种情况可以扩散到下个点
//颜色相同,直接走
if (top.color == mp[nxt.x][nxt.y])
{
nxt.color = mp[nxt.x][nxt.y];
nxt.cost = top.cost;
nxt.step = top.step + 1;
//裁剪,同一个状态下之记录最优的情况去扩散
if (cost[nxt.x][nxt.y][nxt.color] == 0 || nxt.cost < cost[nxt.x][nxt.y][nxt.color])
{
cost[nxt.x][nxt.y][nxt.color] = nxt.cost;
q.push(nxt);
}
}
//目标点是无色的,且上一个点并没有使用魔法
else if (mp[nxt.x][nxt.y] == 0 && top.color == mp[top.x][top.y])
{
nxt.color = top.color;
nxt.cost = top.cost + 2;
nxt.step = top.step + 1;
//裁剪,同一个状态下之记录最优的情况去扩散
if (cost[nxt.x][nxt.y][nxt.color] == 0 || nxt.cost < cost[nxt.x][nxt.y][nxt.color])
{
cost[nxt.x][nxt.y][nxt.color] = nxt.cost;
q.push(nxt);
}
}
//目标点不是 白色,但是是跨颜色走
else if (mp[nxt.x][nxt.y] != 0 && top.color != mp[nxt.x][nxt.y])
{
nxt.color = mp[nxt.x][nxt.y];
nxt.cost = top.cost + 1;
nxt.step = top.step + 1;
//裁剪,同一个状态下之记录最优的情况去扩散
if (cost[nxt.x][nxt.y][nxt.color] == 0 || nxt.cost < cost[nxt.x][nxt.y][nxt.color])
{
cost[nxt.x][nxt.y][nxt.color] = nxt.cost;
q.push(nxt);
}
}
}
}
if (ans != INT_MAX)
cout << ans - 1 << endl;
else
cout << -1 << endl;
return 0;
}
你学废了吗?