有一个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);
}