洛谷 P3956 [NOIP2017 普及组] 棋盘

 最近刚学到DFS,BFS,所以写一下题解,加深一下印象,奥利给!!!

题目描述

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

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

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

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

输入格式

第一行包含两个正整数m,n,以一个空格分开,分别代表棋盘的大小,棋盘上有颜色的格子的数量。

接下来的n行,每行三个正整数x,y,c, 分别表示坐标为(x,y)的格子有颜色c。

其中c=1 代表黄色,c=0 代表红色。 相邻两个数之间用一个空格隔开。 棋盘左上角的坐标为(1,1)(1,1),右下角的坐标为(m,m)。

棋盘上其余的格子都是无色。保证棋盘的左上角,也就是(1,1)(1,1) 一定是有颜色的。

输出格式

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

 

输入输出样例 

 输入                                       输出

5 7                       8
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0

输入                                           输出

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

 

我们可以用BFS来解决这个问题

mp[i][j]表示这个格子的颜色,用    mp[i][j]!=1&&mp[i][j]!=0    来判断该格子是否本身就有颜色。同时我们可以把mp[i][j]的颜色加2,来模拟用魔法创造的有颜色的格子,这样也不会影响刚才的判断条件的正确性。下一个格子如果是魔法创造出来的,把他减去二,就是他暂时的颜色。

money[i][j]表示走到( i,j ) 所需要的最少金钱数,开始时money[i][j]赋值为-1。之后如果碰到money[i][j]值为-1或者可以将money[i][j]更新为更小的情况,就把i,j放入队列中,以便用它来更新别的位置的最小金钱数。

一旦money值发生改动,都必须要重新把它的位置放入队列中,来更新money数组

一次宽搜以后,就可以求出所有位置的最少金钱数了

代码奉上,本人第一次学搜索,可能不是很对,望见谅。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int, int> PII;
queue<PII> q;
int m, n;//m棋盘边数,有n个格有颜色
int mp[110][110];//记录每个位置颜色
int money[110][110];//记录到达每个位置的最小金钱数
int dx[4] = { 0,1,0,-1 }, dy[4] = {1,0,-1,0};//方向数组,方便上下左右移动
bool init(int x, int y) {//判断是否在m*m的格子中,是返回true,不是返回false,防止越界
    if (x >= 1 && x <= m && y >= 1 && y <= m)return true;
    return false;
}
void dfs(int x, int y) {//直接开搜
    q.push({ x,y });//先将第一个位置放进队列
    while (!q.empty()) {//队列不为空时,一直操作
        auto t = q.front(); q.pop();//队头记下并弹出
        int sx = t.first, sy = t.second;//记录队头位置的坐标,防止之后频繁引用
        for (int i = 0; i < 4; i++) {//四个方向分别判断
            int xx = sx + dx[i], yy = sy + dy[i];//每个方向的下一步记下
            if (init(xx, yy)) {//没越界
                if (mp[sx][sy] == 2 || mp[sx][sy] == 3) {//现在所在的格子是施加魔法了的
                    if (mp[xx][yy] != 0 && mp[xx][yy] != 1)continue;
                    //下一个格子仍然没有颜色,换个方向看看能不能往下走
                    else if (mp[sx][sy] - 2 != mp[xx][yy]) {
                    //现在所在的施加魔法的格子的颜色与下一个有颜色的格子的颜色不同
                        if (money[xx][yy] == -1 || money[sx][sy] + 1 < money[xx][yy]) {
                        //能更新就放入队列,用它来更新其他的格子的金钱数
                            money[xx][yy] = money[sx][sy] + 1;
                            q.push({ xx,yy });
                        }
                    }
                    else//施加魔法的格子与下一个格子颜色一致
                        if (money[xx][yy] == -1 || money[sx][sy] < money[xx][yy]) {
                        //一样,能更新就放队列
                            money[xx][yy] = money[sx][sy];
                            q.push({ xx,yy });
                        }
                }
                else {//当前格子不是魔法格,是本身就有颜色的格子
                    if (mp[xx][yy] != 0 && mp[xx][yy] != 1) {//下一格子没颜色
                        if (money[xx][yy] == -1 || money[sx][sy] + 2 < money[xx][yy]) {
                            //同上
                            money[xx][yy] = money[sx][sy] + 2;
                            mp[xx][yy] = mp[sx][sy] + 2;
                            q.push({ xx,yy });
                        }
                    }
                    else if (mp[xx][yy] != mp[sx][sy]) {//下一格子有颜色,但不一样
                        if (money[xx][yy] == -1 || money[sx][sy] + 1 < money[xx][yy]) {
                            money[xx][yy] = money[sx][sy] + 1;
                            q.push({ xx,yy });
                        }
                    }
                    else //下一格子有颜色,且与当前格子一样
                        if (money[xx][yy] == -1 || money[sx][sy] < money[xx][yy]) {
                            //同上
                            money[xx][yy] = money[sx][sy];
                            q.push({ xx,yy });
                    }
                }
            }
            
        }
    }
}
int main() {
    cin >> m >> n;
    memset(mp, -1, sizeof mp);//初始化,mp,money均为-1
    memset(money, -1, sizeof money);
    money[1][1] = 0;//仅(1,1)位置为0
    while (n--) { //给地图上色
        int x, y, c;
        cin >> x >> y >> c;
        mp[x][y] = c;
    }
    dfs(1, 1);//开搜
    cout << money[m][m] << endl;//输出最后一个位置的最小金钱数
    return 0;
    //return 0;华丽收场
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值