璃月森林探险:符文之路

描述

在提瓦特大陆的一片未知之地,探险者需要从神秘的璃月森林的入口走到森林的深处,寻找隐藏在其中的秘密。这片森林被古老的魔法所影响,地面上的石板由两种颜色的符文覆盖——红色和黄色,还有一些未被任何魔法触及的灰色石板。

探险者的每一步都必须踏在有符文的石板上。他们可以向上、下、左、右四个方向行进。当从一个有符文的石板步入另一个有符文的石板时,如果符文颜色相同,则无需任何代价;如果颜色不同,则需支付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种操作:

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

此外再特判一下终点无色的情况,如果终点无色,想要走到终点,当且仅当能够走到终点相邻的格子。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值