描述
在提瓦特大陆的一片未知之地,探险者需要从神秘的璃月森林的入口走到森林的深处,寻找隐藏在其中的秘密。这片森林被古老的魔法所影响,地面上的石板由两种颜色的符文覆盖——红色和黄色,还有一些未被任何魔法触及的灰色石板。
探险者的每一步都必须踏在有符文的石板上。他们可以向上、下、左、右四个方向行进。当从一个有符文的石板步入另一个有符文的石板时,如果符文颜色相同,则无需任何代价;如果颜色不同,则需支付1枚原石作为能量转换的代价。
探险者还拥有一项特殊的能力:他们可以支付2枚原石,使用一次魔法将一块灰色石板暂时变为任意颜色的符文石板,但这种魔法不能连续使用。一旦探险者离开这块暂时变色的石板,石板将恢复成原来的无色状态,并且探险者必须先踏上一个本来就有颜色的符文石板,才能再次使用这项魔法。
探险者的任务是从森林的入口(1,1)出发,到达森林深处的秘密所在地(m,m)。现在探险者需要计划他们的路线,以使支付的原石数量最少。如果无法到达目的地,探险者将返回不可能的信号。
游戏的设置如下:森林地图是一个m×m的方格,每个方格可能被红色或黄色的符文覆盖,或者是灰色的未被覆盖的石板。给定森林中有颜色符文的方格的数量及其位置,探险者需要找出最佳路线
输入
- 第一行包含两个正整数 m 和 n,分别代表森林的大小和有颜色符文的方格的数量。
- 接下来的 n 行,每行三个整数 x, y, c,表示坐标为 (x, y) 的方格上的符文颜色 c,其中 c=1 代表黄色符文,c=0 代表红色符文。
数据范围
1≤m≤100,
1≤n≤1000
输出
- 输出一行,一个整数,表示最小的原石数量。如果无法到达目的地,输出 -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)走到(𝑚,𝑚),你只能走在有颜色的格子中,并且使得你所花费的代价最小。
当你站在一个有颜色的格子上的时候,你可以进行如下2种操作:
- 向上、下、左、右前行。
- 向左上、左下、右上、右下、向上连跳2格、向下连跳2格、向左连跳2格、向右连跳2格前行。
如果你使用的是操作 2 ,你将额外付出 2 点代价。
在一次操作中,如果你这次操作的起点格子与终点格子颜色不同,你将付出1点代价。
然后维护一个优先队列,采用最优消耗算法,对本题展开bfs。可以理解为一种特殊的分支限界。
代码实现
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<stdio.h>
using namespace std;
#define INF 0x3f3f3f3f
int n,m;
int Forest[105][105];
int dist[105][105];
struct Node
{
int x,y,w;
bool operator<(Node b)const{
return w>b.w;
}//优先队列默认取出最大元素
};
priority_queue<Node> q;
int dx[12]={0,1,0,-1,1,1,-1,-1,0,2,0,-2};//12方向及魔法代价
int dy[12]={1,0,-1,0,1,-1,1,-1,2,0,-2,0};
int dw[12]={0,0,0,0,2,2,2,2,2,2,2,2};
void bfs(){
memset(dist,0x3f,sizeof(dist));dist[1][1]=0;
q.push((Node){1,1,dist[1][1]});
Node cur,next;
while(!q.empty()){
cur=q.top();q.pop();
if(dist[cur.x][cur.y]<cur.w)continue;
for(int i=0;i<12;i++){//懒惰删除
next.x=cur.x+dx[i],next.y=cur.y+dy[i],next.w=cur.w+dw[i];
if(next.x<=0||next.x>m||next.y<=0||next.y>m)continue;//保证在棋盘范围内
if(!Forest[next.x][next.y])continue;
if(Forest[cur.x][cur.y]!=Forest[next.x][next.y])next.w++;//确定下一步的信息
if(dist[next.x][next.y]>next.w){
dist[next.x][next.y]=next.w;
q.push(next);//这里的push是拷贝变量,不需要担心变量的修改
}
}
}
}
int main()
{
scanf("%d %d",&m,&n);
for(int i=0;i<n;i++)
{
int x,y,c;
scanf("%d %d %d",&x,&y,&c);
Forest[x][y]=c+1;
}
bfs();
if(!Forest[m][m]){//处理(m,m)无色情况
int ans=min(dist[m][m-1],dist[m-1][m])+2;
if(ans>=INF)puts("-1");
else printf("%d\n",ans);
}
else{
if(dist[m][m]==INF)puts("-1");
else printf("%d\n",dist[m][m]);
}
return 0;
}
此外再特判一下终点无色的情况,如果终点无色,想要走到终点,当且仅当能够走到终点相邻的格子。