[CCF-1142]棋盘 题解【BFS + 记忆化搜索】

制作不易,点个赞吧!来源: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

提示

棋盘.png

【输入输出样例说明】

从(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;
} 




你学废了吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值