P3956 [NOIP2017 普及组] 棋盘(dfs加剪枝)

题目背景

NOIP2017 普及组 T3

题目描述

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

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

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

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

输入格式

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

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

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

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

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

输出 #2复制

-1

说明/提示

输入输出样例 1 说明

从(1,1)(1,1)开始,走到(1,2)(1,2)不花费金币

从(1,2)(1,2)向下走到(2,2)(2,2)花费 11 枚金币

从(2,2)(2,2)施展魔法,将(2,3)(2,3)变为黄色,花费 22 枚金币

从(2,2)(2,2)走到(2,3)(2,3)不花费金币

从(2,3)(2,3)走到(3,3)(3,3)不花费金币

从(3,3)(3,3)走到(3,4)(3,4)花费 11 枚金币

从(3,4)(3,4)走到(4,4)(4,4)花费 11 枚金币

从(4,4)(4,4)施展魔法,将(4,5)(4,5)变为黄色,花费22 枚金币,

从(4,4)(4,4)走到(4,5)(4,5)不花费金币

从(4,5)(4,5)走到(5,5)(5,5)花费 11 枚金币

共花费 88枚金币。

输入输出样例 2 说明

从( 1, 1)(1,1)走到( 1, 2)(1,2),不花费金币

从( 1, 2)(1,2)走到( 2, 2)(2,2),花费11金币

施展魔法将( 2, 3)(2,3)变为黄色,并从( 2, 2)(2,2)走到( 2, 3)(2,3)花费22 金币

从( 2, 3)(2,3)走到( 3, 3)(3,3)不花费金币

从( 3, 3)(3,3)只能施展魔法到达( 3, 2),( 2, 3),( 3, 4),( 4, 3)(3,2),(2,3),(3,4),(4,3)

而从以上四点均无法到达( 5, 5)(5,5),故无法到达终点,输出-1−1

数据规模与约定

对于 30\%30%的数据, 1 ≤ m ≤ 5, 1 ≤ n ≤ 101≤m≤5,1≤n≤10。

对于 60\%60%的数据, 1 ≤ m ≤ 20, 1 ≤ n ≤ 2001≤m≤20,1≤n≤200。

对于 100\%100%的数据, 1 ≤ m ≤ 100, 1 ≤ n ≤ 1,0001≤m≤100,1≤n≤1,000。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll Inf = 0x3f3f3f3f3f3f3f3f;
int res = inf;
int e[109][109];
int f[109][109];
int vis[109][109];
int m, n;  //参数:当前所在位置,当前位置的颜色,走到当前位置已花费money
void dfs(int curx, int cury, int color, int money)
{
    if (curx == m && cury == m) {
        res = min(res, money);
        return;
    }
    int nxt[4][2] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
    int i;
    for (i = 0; i < 4; i++) {
        int tx = curx + nxt[i][0];
        int ty = cury + nxt[i][1];
        if (tx < 1 || ty < 1 || tx > m || ty > m)
            continue;
        if (e[curx][cury] == -1 && e[tx][ty] == -1)//不能连用魔法
            continue;
        if (e[tx][ty] != -1) {
            if (color == e[tx][ty]) {              //剪枝,两个条件
                if (vis[tx][ty] == 0 && money < f[tx][ty] && money < res) {
                    vis[tx][ty] = 1;
                    f[tx][ty]=money;
                    dfs(tx, ty, e[tx][ty], money);
                    vis[tx][ty] = 0;
                }
            } else {
                if(vis[tx][ty]==0&&money+1<f[tx][ty]&&money+1<res){
                    vis[tx][ty]=1;
                    f[tx][ty]=money+1;
                    dfs(tx,ty,e[tx][ty],money+1);
                    vis[tx][ty]=0;
                }
            }
        } else {
            if (vis[tx][ty] == 0 && money + 2 < f[tx][ty] && money + 2 < res) {
                vis[tx][ty] = 1;
                f[tx][ty]=money+2;
                dfs(tx, ty, color, money + 2);
                vis[tx][ty] = 0;
            }
        }
    }
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> m >> n;
    memset(e, -1, sizeof(e));
    int i;
    for (i = 1; i <= n; i++) {
        int x, y, c;
        cin >> x >> y >> c;
        e[x][y] = c;
    }
    memset(f, 0x3f, sizeof(f));
    dfs(1, 1, e[1][1], 0);
    if(res!=inf)
    cout<<res;
    else cout<<-1;
    return 0;
}

### NOIP 2017 棋盘题目解析 #### 题目背景与描述 在NOIP 2017普及组比赛中,棋盘问题是其中一道具有挑战性的试题。该问题主要考察选手对于深度优先搜索(DFS)及其优化技巧的理解和应用能力[^1]。 #### 考察知识点 此题重点在于深搜算法的应用以及如何通过合理的策略减少不必要的计算量——即所谓的“剪枝”。具体来说: - **深搜回溯**:当遇到无法继续放置新棋子的情况时,应回退至上一步并尝试其他可能性。 - **最优化剪枝**:为了提高效率,在遍历过程中应尽早排除明显不可能成功的路径。 #### 解决方案概述 针对这个问题的一个有效解决方案是采用基于DFS的方法,并结合特定条件下的提前终止机制来进行性能上的改进。以下是实现这一方法的关键要点: - 使用二维数组表示棋盘状态; - 对于每一个可能的位置,判断是否可以安全地放下当前类型的棋子; - 如果当前位置合法,则更新棋盘并将新的位置作为起点递归调用函数处理后续操作; - 当发现某个分支不再满足获胜条件时立即停止探索这条路线,转而测试其它选项; - 记录下所有成功完成布局的方式数量作为最终答案返回。 ```cpp #include <iostream> using namespace std; const int N = 8; int n, m, k; // 定义全局变量n,m,k分别代表行数、列数和需要摆放的皇后数目 bool row[N], col[N], diag1[2*N-1], diag2[2*N-1]; // 行、列、两个方向对角线占用情况标记表 int ans = 0; void dfs(int r){ if(r == n){ // 找到了一种可行解法 ++ans; return ; } for (int c=0;c<m;++c){ if (!row[r] && !col[c] && !diag1[r+c] && !diag2[r-c+n-1]){ // 判断能否在此处放一个棋子 row[r]=true,col[c]=true,diag1[r+c]=true,diag2[r-c+n-1]=true; // 更新各维度的状态 dfs(r+1); // 继续向下一层递归 row[r]=false,col[c]=false,diag1[r+c]=false,diag2[r-c+n-1]=false; // 回溯恢复现场 } } } int main(){ cin >> n >> m >> k; dfs(0); cout << ans; } ``` 上述代码片段展示了利用DFS解决N皇后的经典框架,虽然不是完全适用于原版棋盘问题的具体细节,但是体现了相似的核心逻辑和技术手段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值