棋盘(dfs+记忆化搜索)

该问题是一个寻路问题,涉及到颜色匹配和魔法使用策略。给定一个m×m的棋盘,需要从左上角到右下角,经过有颜色的格子,颜色相同时不花费金币,不同则需花费1个金币。可以使用魔法使无色格子短暂变色,但需花费2个金币且不能连续使用。通过记忆化搜索算法找到最低花费路径,示例给出了不同情况下的解决方案和解释。
摘要由CSDN通过智能技术生成

有一个m × m的棋盘,棋盘上每一个格子可能是红色、黄色或没有任何颜色的。你现在要从棋盘的最左上角走到棋盘的最右下角。

任何一个时刻,你所站在的位置必须是有颜色的(不能是无色的),你只能向上、下、左、右四个方向前进。当你从一个格子走向另一个格子时,如果两个格子的颜色相同,那你不需要花费金币;如果不同,则你需要花费 1 个金币。

另外,你可以花费 2 个金币施展魔法让下一个无色格子暂时变为你指定的颜色。但这个魔法不能连续使用,而且这个魔法的持续时间很短,也就是说,如果你使用了这个魔法,走到了这个暂时有颜色的格子上,你就不能继续使用魔法;只有当你离开这个位置,走到一个本来就有颜色的格子上的时候,你才能继续使用这个魔法,而当你离开了这个位置(施展魔法使得变为有颜色的格子)时,这个格子恢复为无色。

现在你要从棋盘的最左上角,走到棋盘的最右下角,求花费的最少金币是多少?

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

输入描述:

数据的第一行包含两个正整数 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

输出

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枚金币。

示例2

输入

5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0

输出

-1

说明

 

从(1,1)走到(1,2),不花费金币从(1,2)走到(2,2),花费 1金币施展魔法将(2,3)变为黄色,并从(2,2)走到(2,3)花费 2金币从(2,3)走到(3,3)不花费金币

从(3,3)只能施展魔法到达(3,2),(2,3),(3,4),(4,3)

而从以上四点均无法到达(5,5),故无法到达终点,输出-1

备注:

对于 30%的数据,1≤m ≤5, 1 ≤ n ≤ 10。
对于 60%的数据,1≤m ≤20, 1 ≤ n ≤ 200。
对于 100%的数据,1 ≤ m ≤ 100, 1 ≤ n ≤ 1,000。

思路:

1、输入数据,构造棋盘,总共三种颜色,0表示红色,1表示黄色,-1表示无色;

2、从(1,1)点开始,往上下左右四个方向去搜索,这里和普通的只能往右和左搜索的题目有点不同,在普通的搜索里面,到达了一个点就设一个标志变量,然后下次就不再搜索这个节点,但是这里不能这么简单的处理,因为每个节点可以往上下左右四个方向搜索,如果不设标志则会形成死循环,出不来,设个标志则有可能从不同的路径走到这个点的花费可能不相同,第一次的花费不一定是最低的。那么我们就把进到这个节点的花费记录下来,下次进入这个节点时候,比较一下花费,如果相同或者大于上次花费,就不用搜索这个节点,否则就继续搜索他,我们把这种方法叫做记忆化搜索

3、对于魔法问题,我们采用一点贪心策略,碰到一个无色格子,就让他变得和当前格子颜色一样(这样的话就能保证当前最小,所有都最小,故最后最小),再到深搜递归函数里面加上一个参数,来表示魔法状态,如果上次已经使用了魔法,而当前格子是无色,也需要使用魔法,因为不能两次使用魔法,就直接返回。

4、当走到了右下角(m,m)点,说明已经找到了一条路径,把花费最小那条路径记录下来就OK了。
当时我做的时候用bfs出现一个误区 只想着到那个点就是最少花费了,其实不是 那是最小步数不是最小花

DFS

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
int map[N][N];//棋盘数据
int dist[N][N];//到达某个格子花费的最少金币数
int m,n;
int dx[]={0,0,-1,1},dy[]={1,-1,0,0};
int ans=0x3f3f3f3f;//全局变量存最终结果
//横、纵坐标,是否使用过魔法,花费金币总数 
//因为若若是空格使用魔法 但是不能联系使用两次 故要标记一下上次是否勇敢魔法
void dfs(int x,int y,bool magic,int sum)
{
	//记忆化搜索 如果这次走到(x,y)花的金币不比之前搜索的少,必定不是最优解
	if(dist[x][y]<=sum) return ;
	// 否则更新最优解
	dist[x][y]=sum;
	//判断是否到终点,是则更新答案 
	if(x==m&&y==m)
	{
		ans=min(ans,sum);
		return ;
	}
	for(int i=0;i<4;i++)
	{
		int xx=x+dx[i],yy=y+dy[i];
		if(xx<1||xx>m||yy<1||y>m) continue;
		//如果是空格
		if(map[xx][yy]==2)
		{
			   //如果上次使用过魔法,此路不通
			if(magic) continue;
			else
			{
				//上次没用魔法,可以用
				 //因为要花最少金币,所以指定颜色和过来的格子一样
				map[xx][yy]=map[x][y];
				dfs(xx,yy,true,sum+2);//走到下一个格子上
				map[xx][yy]=2;//恢复空白
			}
		}
		else//如果不是空格
		{
			if(map[x][y]==map[xx][yy]) dfs(xx,yy,false,sum);//颜色相同
			else dfs(xx,yy,false,sum+1);//颜色不同
		}
	}
	return ;
}
int main()
{
	cin>>m>>n;
	//这个地方用memset不对 不知到为什么 map[][]=2表示空白 把所有的距离初始化最大
	for(int i=0;i<N;i++)
	for(int j=0;j<N;j++)
	map[i][j]=2,dist[i][j]=0x3f3f3f3f;
	{
		int a,b,c;cin>>a>>b>>c;
		map[a][b]=c;
	}
	dfs(1,1,false,0);
	if(ans!=0x3f3f3f3f) cout<<ans<<endl;
	else cout<<"-1"<<endl;
	return 0;
}

BFS

#include <cstdio>
#include <queue> 
#include <cmath>

using namespace std;

const int INF = 0x7ffffff;
const int N = 105;
struct T {
	int xp, yp, v, c;
	//x,y坐标,价格,颜色 
};

int m, n, x, y, c;
int a[N][N], val[N][N], ans = -1; 
// a数组存储原图,相当于前面说明的map
// val存储最小花费
int dx[4] = {1, -1, 0, 0};
int	dy[4] = {0, 0, 1, -1};
queue<T> Q;

void bfs() {
	val[1][1] = 0;
	Q.push(T{1, 1, 0, a[1][1]});
	while (!Q.empty()) {
		T head = Q.front();
		Q.pop();
		int bx = head.xp, by = head.yp;
		int bv = head.v, bc = head.c;
		// 上一个访问点的x,y坐标、最小花费和颜色
		for (int i = 0; i < 4; i++) {
			int cx = bx + dx[i];
			int cy = by + dy[i];
            // cx,cy为目前站在的点的x,y坐标
			if (cx > 0 && cy > 0 && cy <= m && cy <= m) {// 是否越界
				if (a[cx][cy]){
					if (val[cx][cy] > bv + abs(bc - a[cx][cy])) {// 更新最小花费
						val[cx][cy] = bv + abs(bc - a[cx][cy]);
						Q.push(T{cx, cy, val[cx][cy], a[cx][cy]});
					}
				}
				else {
					if (!a[bx][by]) continue ;
					if (val[cx][cy] > bv + 2) {// 同上
						val[cx][cy] = bv + 2;
						Q.push(T{cx, cy, val[cx][cy], bc});
					}
				}
			}
		}
	}
	if (val[m][m] != INF) ans = val[m][m];
    // 若能够到达终点
	return ;
}

int main() {
	scanf("%d %d", &m, &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d %d %d", &x, &y, &c);
		a[x][y] = c + 1;
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= m; j++) {
			val[i][j] = INF;
		}
	}
	
	bfs();
	
	printf("%d", ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值