bfs与优先队列 [NOIP2017 普及组] 棋盘————洛谷p3956

[NOIP2017 普及组] 棋盘

题目背景

NOIP2017 普及组 T3

题目描述

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

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

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

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

输入格式

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

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

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

棋盘上其余的格子都是无色。保证棋盘的左上角,也就是 \((1, 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

样例输出 #1

8

样例 #2

样例输入 #2

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

样例输出 #2

-1

提示

样例 1 说明

棋盘的颜色如下表格所示,其中空白的部分表示无色。

\(\color{red}\text{红}\) \(\color{red}\text{红}\)
\(\color{yellow}\text{黄}\)
\(\color{yellow}\text{黄}\) \(\color{red}\text{红}\)
\(\color{yellow}\text{黄}\)
\(\color{red}\text{红}\)

\((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 说明

棋盘的颜色如下表格所示,其中空白的部分表示无色。

\(\color{red}\text{红}\) \(\color{red}\text{红}\)
\(\color{yellow}\text{黄}\)
\(\color{yellow}\text{黄}\)
\(\color{white}\text{ }\)
\(\color{red}\text{红}\)

\(( 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\)

分享一个来自洛谷大佬https://www.luogu.com.cn/user/35871的代码,真的很厉害,有时候将问题转化一下,真的可以极大的简化问题

#include<iostream>
#include<queue>
using namespace std;
#define inf 0x3f3f3f
template<typename Tp>
//快速读取
void read(Tp& x) {
    char c = getchar();
    x = 0;
    int f = 1;
    whiel(c < '0' && c>'9') {
        if (c == '_') {
            f = -1;
        }
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    x *= f;
}
struct node {
    int x, y, c, w;
    bool operator < (node a)const {
        return w > a.w;
    }
    //stl中优先队列默认优先取出最大值,所以要重载运算符,保证每次取出的都是代价最小的元素
};
priority_queue<node> q;
//将魔法操作当成两步来算,施展魔法跳过去,然后离开,由此多出八个方向,包括上下左右总共12个方向
int dx[] = { 0,1,0,-1,1,1,-1,-1,0,2,0,-2 };
int dy[] = { 1,0,-1,0,1,-1,1,-1,2,0,-2,0 };
int dw[] = { 0,0,0,0,2,2,2,2,2,2,2,2 };
int a[105][105], dis[105][105];
int n, m;
void bfs() {
    memset(dis, 0x3f, sizeof(dis));
    dis[1][1] = 0;
    node cur, nxt;
    while (!q.empty()) {
        cur = q.top(); //利用优先队列优先取出更短路
        q.pop();
        if (dis[cur.x][cur.y] < cur.w) continue;
        for (int i = 0; i < 12; i++) {
            nxt.x = cur.x + dx[i];
            nxt.y = cur.y + dy[i];
            if (nxt.x <= 0 || nxt.x > m || nxt.y <= 0 || nxt.y > m) continue;
            nxt.w = cur.w + dw[i];
            nxt.c = a[nxt.x][nxt.y];
            if (!nxt.c) continue; //只有两种情况,一是当只走一步时碰见空白格,其实可以等效为走两格,因为它迟早要离开,下一步只能走一格,二是走两格时碰见空白格,由于走两格时就使用了一次魔法,再碰到空白格就不可以走了,所以也是continue
            if (cur.c != nxt.c) nxt.w++;
            if (dis[nxt.x][nxt.y] > nxt.w) {
                dis[nxt.x][nxt.y] = nxt.w;
                q.push(nxt);
            }
        }
    }
}

int main() {
    int x, y, c;
    read(m); read(n);
    for (int i = 1; i <= n; i++) {
        read(x), read(y), read(c);
        a[x][y] = c + 1;//利用c+1区别无颜色格子
        //c=0代表该格子无颜色
        //c=1代表该格子为红色
        //c=2代表该格子为黄色
    }
    bfs();
    //处理无色情况
    if (!a[m][m]) {
        //如果a[m][m]为无色,那么根据算法dis[m][m]是不会被更改的,还是inf,但实际上还是有可能到达的,所以需要单独处理下
        //如果与他相邻的位置均为无色,那么肯定到不了,如果有颜色则找到端短路,然后加上魔法使用的2
        int ans = min(dis[m - 1][m], dis[m][m - 1]) + 2;
        if (ans >= inf) {
            puts("-1");
        }
        else {
            printf("%d\n", ans);
        }
    }
    else {
        if (dis[m][m] == inf) {
            puts("-1");
        }
        else {
            printf("%d\n", dis[m][m]);
        }
    }
    return 0;
}

请登录后发表评论

    没有回复内容