zoukankan      html  css  js  c++  java
  • 洛谷P3956「NOIP2017普及组」《棋盘》

    原创建时间:2018-10-02 22:23:11

    三种搜索剪枝

    题目链接

    题目描述

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

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

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

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

    Input / Output 格式 & 样例

    输入格式

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

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

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

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

    输出格式

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

    输入样例

    Case #1:

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

    Case #2:

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

    输出样例

    Case #1:

    8
    

    Case #2:

    -1
    

    样例解释 & 其他说明

    对于 (30\%)的数据, (1 ≤ m ≤ 5, 1 ≤ n ≤ 10)

    对于 (60\%)数据, (1 ≤ m ≤ 20, 1 ≤ n ≤ 200)

    对于 (100\%)的数据, (1 ≤ m ≤ 100, 1 ≤ n ≤ 1,000)

    解析

    我们并不需要维护某一个点是否走过

    我们需要判断边界、白格子、最优性剪枝和走到终点四种情况

    (mp)数组存图,规定0表示白色,1表示红色,2表示黄色

    (f_{i,j})表示(1,1)(i,j)的最少花费

    本题主要的难点在于加入了膜法机制

    那么DFS需要传递四个参数:

    • intx坐标和y坐标
    • int当前使用的金币数量
    • bool当前是否使用了膜法

    在四向DFS中,需要进行以下几点判断:

    • 当前格是否有颜色
      若无颜色且并未使用膜法,则使用膜法,使用金币数量+2,继续DFS;
      若无颜色且使用过膜法,没救了
    • 当前格颜色和下一格颜色是否相同
      若颜色相同,直接进行下一步DFS;
      若颜色不同,使用金币数量+1,继续DFS

    要注意的是,(f)数组的赋值要在判断是否走到终点之前,最优性剪枝之后,不然可能出现赋值不上的情况

    代码实现

    /* -- Basic Headers -- */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    
    /* -- STL Iterator -- */
    #include <vector>
    #include <string>
    #include <stack>
    #include <queue>
    
    /* -- Defined Functions -- */
    #define For(a,x,y) for (int a = x; a <= y; ++a)
    #define Bak(a,y,x) for (int a = y; a >= x; --a)
    using namespace std;
    
    namespace FastIO {
        void DEBUG(char comment[], int x) {
            cerr << comment << x << endl;
        }
    
        inline int getint() {
            int s = 0, x = 1;
            char ch = getchar();
            while (!isdigit(ch)) {
                if (ch == '-') x = -1;
                ch = getchar();
            }
            while (isdigit(ch)) {
                s = s * 10 + ch - '0';
                ch = getchar();
            }
            return s * x;
        }
        inline void __basic_putint(int x) {
            if (x < 0) {
                x = -x;
                putchar('-');
            }
            if (x >= 10) __basic_putint(x / 10);
            putchar(x % 10 + '0');
        }
    
        inline void putint(int x, char external) {
            __basic_putint(x);
            putchar(external);
        }
    }
    
    namespace Solution {
        const int MAXM = 100 + 10;
        
        int f[MAXM][MAXM];
        int mp[MAXM][MAXM];
        #define WHITE 0
        #define RED 1
        #define YELLOW 2
        
        const int dx[5] = {0,  0, 0, -1, 1};
        const int dy[5] = {0, -1, 1,  0, 0};
        
        int m, n, ans = 2147482333;
        
        void DaFaShi(int x, int y, int nowSum, bool usedMogic) {
            // 苟利国家生死以
            // 岂因祸福避趋之
            // 你们啊,不要总是想弄个大新闻 
            // 说什么使用膜法
            // 再把我批判一番 
            if (x < 1 || y < 1 || x > m || y > m) return; // 边界 
            if (mp[x][y] == WHITE) return; // 走到白格子 
            if (nowSum >= f[x][y]) return; // 最优性剪枝 
            f[x][y] = nowSum;
            if (x == m && y == m) {
                ans = std::min(nowSum, ans);
                return;
                // 搜索完成 
            }
            
            For (i, 1, 4) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if (mp[nx][ny] != WHITE) {
                    // 有颜色 
                    if (mp[nx][ny] == mp[x][y]) DaFaShi(nx, ny, nowSum, false);
                    // 颜色相同,继续往后搜 
                    else DaFaShi(nx, ny, nowSum + 1, false); // 颜色不同,花费金币 
                } else if (mp[nx][ny] == WHITE && !usedMogic){
                    // 没颜色且没用膜法 
                    mp[nx][ny] = mp[x][y]; // 念诗,使用膜法 
                    DaFaShi(nx, ny, nowSum + 2, true); // 使用膜法花费2金币 
                    mp[nx][ny] = WHITE; // 回溯 
                }
            }
        }
    }
    
    int main(int argc, char *const argv[]) {
        #ifdef HANDWER_FILE
        freopen("testdata.in", "r", stdin);
        freopen("testdata.out", "w", stdout);
        #endif
        using namespace Solution;
        using namespace FastIO;
        memset(f, 0x7f, sizeof(f));
        m = getint();
        n = getint();
        For (i, 1, n) {
            int x, y, c;
            x = getint();
            y = getint();
            c = getint();
            mp[x][y] = c + 1;
        }
        DaFaShi(1, 1, 0, false);
        if (ans == 2147482333) puts("-1");
        else putint(ans, '
    ');
        return 0;
    }
    
    
  • 相关阅读:
    快速指引(CDH6.3.2)
    gRpc 跨语言调用(NetCore 与 Spring Boot)
    Windows 极简利器
    Jenkins 于Docker 中源配置
    Kettle 问题
    在 Ubuntu 下直接将二进制文件制作成 rpm 包
    麒麟常见问题
    基于jssip的简单封装
    带有handleEvent的eventEmitter
    js集锦
  • 原文地址:https://www.cnblogs.com/handwer/p/11745446.html
Copyright © 2011-2022 走看看