[NOIP2017]棋盘

链接:https://ac.nowcoder.com/acm/problem/16423
来源:牛客网
 

题目描述

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

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

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

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

输入描述:

数据的第一行包含两个正整数 m,n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。
接下来的 n 行,每行三个正整数 x,y,c,分别表示坐标为(x,y)的格子有颜色 c。其中 c=1代表黄色,c=0 代表红色。相邻两个数之间用一个空格隔开。棋盘左上角的坐标为(1,1),右下角的坐标为(m, m)。
棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(1,1)一定是有颜色的。

输出描述:

输出一行,一个整数,表示花费的金币的最小值,如果无法到达,输出-1。

这题可以用BFS+优先队列。
对于每一步有以下几种情况:
下一步走红色:如果当前颜色是黄色,那总花费(val)+1, 如果当前颜色是红色,val不变 
下一步走黄色:如果当前颜色是红色,那val+1, 如果当前颜色是黄色,val不变
下一步走无色:如果当前颜色是红色,我们把下一步无色变红色,val+2, 把无色变黄色,val+3
                         如果当前颜色是黄色,我们把下一步无色变黄色,val+2, 把无色变红色,val+3


我们每走一步就把当前的情况存入优先队列里,按当前val的值从小到大排序,当优先队列最先弹出来的坐标为(m,m)的值时,该val一定是所有到(m,m)情况中最小的。 
同时,由于变色的操作不能连续使用,所以我们应该开个变量记录上一步是否变了色,代码如下。  

代码:

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 105;
struct node{
    int x, y, col, val, status; //记录坐标,当前格子的颜色,当前的花费,以及上一步是否变色的状态:0-没变,1-变了 
    bool operator < (const node& a) const
    {
        return val > a.val;
    }
};
int px[] = {0, 0, -1, 1};
int py[] = {1, -1, 0, 0};

priority_queue<node> q;
int Map[N][N], fg[N][N] {0}; //存图和标记该格子是否走过 
int res = 0;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int m, n;
    cin >> m >> n;
    memset(Map, -1, sizeof(Map)); //初始化为-1,-1表示无色的格子 
    for(int i=0; i<n; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        Map[x][y] = z; //存图
    }
    q.push({1, 1, Map[1][1], 0, 0});//初始状态,没有变色 
    int ret = 0; //标记是否能够到达(m,m) 
    while(!q.empty())
    {
        node k = q.top();
        if(k.x==m && k.y==m) //当到达时 
        {
        	res = k.val;
            ret = 1; //能够到达时标记 
        	break;
		}
        q.pop();
        for(int i=0; i<4; i++)
        {
            int x = k.x + px[i];
            int y = k.y + py[i];
            if(x>0 && y>0 && x<=m && y<=m && !fg[x][y])
            {
                if(Map[x][y]==0)
                    q.push({x, y, 0, (k.col==0?k.val:k.val+1), 0}), fg[x][y] = 1;
                if(Map[x][y]==1)
                    q.push({x, y, 1, (k.col==1?k.val:k.val+1), 0}), fg[x][y] = 1;
                if(Map[x][y]==-1 && !k.status)
                {
                    q.push({x, y, 0, (k.col==0?k.val+2:k.val+3), 1});
                    q.push({x, y, 1, (k.col==1?k.val+2:k.val+3), 1});
                    fg[x][y] = 1;
                }
            }
        }
    }
    if(ret)
        cout << res << endl;
    else cout << -1 << endl;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值