首页 > 其他分享 >CSP历年复赛题-P3956 [NOIP2017 普及组] 棋盘

CSP历年复赛题-P3956 [NOIP2017 普及组] 棋盘

时间:2024-06-07 16:56:18浏览次数:28  
标签:NOIP2017 一步 int sum 魔法 nx ny P3956 CSP

原题链接:https://www.luogu.com.cn/problem/P3956

题意解读:计算从(1,1)走到(m,m)的最小花费,有几个限定:

同色格子可以走,花费为0;

不同色格子可以走,花费为1;

有色格子可以走到无色格子,花费为2,且用将无色格子临时染色;

无色格子不能走到无色格子。

解题思路:

可以采用DFS来暴搜所有路径,需要四个状态:

x:横坐标

y:纵坐标

sum:花费的总金币

use:走到(x,y)是否使用了魔法

从起点开始,枚举上下左右四个位置,判断是否能走:

1、超出范围,不能走

2、已经走过,不能走

3、上一步使用了魔法,下一步位置无色,不能走

4、上一步没有使用魔法,下一步位置无色,可以使用魔法,标记成跟上一步一样的颜色

5、下一步位置有色,根据上一步的颜色计算花费

注意:对于黄色设置为1,红色设置为0,无色设置为-1

55分代码:

#include <bits/stdc++.h>
using namespace std;

const int M = 105;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, -1, 0, 1};
int m, n;
int a[M][M];
bool vis[M][M]; //标记是否已走过
int x, y, c;
int ans = INT_MAX;

//sum:花费的金币  use:是否使用魔法
void dfs(int x, int y, int sum, bool use)
{
    if(x == m && y == m)
    {
        ans = min(ans, sum);
        return;
    }
    for(int i = 0; i < 4; i++)
    {
        int nx = x + dx[i], ny = y + dy[i];
        if(nx < 1 || nx > m || ny < 1 || ny > m || vis[nx][ny]) continue;
        if(a[nx][ny] == -1 && use) continue; //上一步是使用魔法到的且下一步无颜色

        vis[nx][ny] = true;
        if(a[nx][ny] == -1) //下一步无颜色,可以使用魔法
        {
            a[nx][ny] = a[x][y]; //用魔法变颜色,变成和上一步相同的颜色最好
            dfs(nx, ny, sum + 2, true); //使用魔法走到nx,ny
            a[nx][ny] = -1; //恢复
        }
        else //下一步有颜色
        {
            if(a[x][y] == a[nx][ny]) dfs(nx, ny, sum, false); //与上一步颜色相同
            else dfs(nx, ny, sum + 1, false); //与上一步颜色不同
        }
        vis[nx][ny] = false; //恢复
    }
}

int main()
{
    cin >> m >> n;
    memset(a, -1, sizeof(a));
    memset(f, 0x3f, sizeof(f));
    for(int i = 1; i <= n; i++)
    {
        cin >> x >> y >> c;
        a[x][y] = c;
    }

    vis[1][1] = true;
    dfs(1, 1, 0, false);

    if(ans == INT_MAX) cout << -1;
    else cout << ans;

    return 0;
}

由于DFS枚举的所有可能,会导致部分数据超时,需要引入剪枝方法

这里只需要一种简单的判断,当走到(x, y)时,记录下最少的花费sum,如果下一次再走到(x, y),之前记录的花费都不超过当前的花费,则没有必要再继续DFS,可以提前结束。

只需要引入一个int f[M][M]

当f[x][y] <= sum的时候提前结束,否则就记录f[x][y] = sum

注意f[x][y]保存的是更小的sum,因此要初始化为极大值memset(f, 0x3f, sizeof(f))

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int M = 105;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, -1, 0, 1};
int m, n;
int a[M][M];
bool vis[M][M]; //标记是否已走过
int x, y, c;
int ans = INT_MAX;
int f[M][M]; //记录走到i,j时的最小花费,初始化为极大值

//sum:花费的金币  use:是否使用魔法
void dfs(int x, int y, int sum, bool use)
{
    if(f[x][y] <= sum) return; //如果之前走过x,y,且花费更小,则不用继续了
    f[x][y] = sum; //保存更小的花费
    
    if(x == m && y == m)
    {
        ans = min(ans, sum);
        return;
    }
    for(int i = 0; i < 4; i++)
    {
        int nx = x + dx[i], ny = y + dy[i];
        if(nx < 1 || nx > m || ny < 1 || ny > m || vis[nx][ny]) continue;
        if(a[nx][ny] == -1 && use) continue; //上一步是使用魔法到的且下一步无颜色

        vis[nx][ny] = true;
        if(a[nx][ny] == -1) //下一步无颜色,可以使用魔法
        {
            a[nx][ny] = a[x][y]; //用魔法变颜色,变成和上一步相同的颜色最好
            dfs(nx, ny, sum + 2, true); //使用魔法走到nx,ny
            a[nx][ny] = -1; //恢复
        }
        else //下一步有颜色
        {
            if(a[x][y] == a[nx][ny]) dfs(nx, ny, sum, false); //与上一步颜色相同
            else dfs(nx, ny, sum + 1, false); //与上一步颜色不同
        }
        vis[nx][ny] = false; //恢复
    }
}

int main()
{
    cin >> m >> n;
    memset(a, -1, sizeof(a));
    memset(f, 0x3f, sizeof(f));
    for(int i = 1; i <= n; i++)
    {
        cin >> x >> y >> c;
        a[x][y] = c;
    }

    vis[1][1] = true;
    dfs(1, 1, 0, false);

    if(ans == INT_MAX) cout << -1;
    else cout << ans;

    return 0;
}

 

标签:NOIP2017,一步,int,sum,魔法,nx,ny,P3956,CSP
From: https://www.cnblogs.com/jcwy/p/18237492

相关文章

  • CSP历年复赛题-P3955 [NOIP2017 普及组] 图书管理员
    原题链接:https://www.luogu.com.cn/problem/P3955题意解读:给出n个图书编号,q个需求码,找到后缀与需求码匹配的最小图书编号,没有输出-1。解题思路:先对图书编号排序,用枚举法遍历每一个图书编号,看后缀是否与需求码相同。100分代码:#include<bits/stdc++.h>usingnamespacestd;c......
  • CSP历年复赛题-P2119 [NOIP2016 普及组] 魔法阵
    原题链接:https://www.luogu.com.cn/problem/P2119题意解读:在一组数里找出所有的Xa,Xb,Xc,Xd的组合,使得满足Xa<Xb<Xc<Xd,Xb-Xa=2(Xd-Xc),Xb-Xa<(Xc-Xb)/3,并统计出每个数作为A,B,C,D出现的次数。解题思路:1、枚举(O(n^4))首先想到的是通过4重循环枚举所有可能的Xa,Xb,Xc,Xd,然后判......
  • CSP历年复赛题-P2058 [NOIP2016 普及组] 海港
    原题链接:https://www.luogu.com.cn/problem/P2058题意解读:计算24小时时间窗口内不同国家的数量,是队列的典型应用。解题思路:本题需要用到两个关键的数据结构:队列、数组队列用来保存24小时内到达的船的时间,数组用来保存24小时内每个国家有多少人每到一只船,需要把时间放入队列,如......
  • CSP历年复赛题-P2010 [NOIP2016 普及组] 回文日期
    原题链接:https://www.luogu.com.cn/problem/P2010题意解读:计算两个日期之间有多少个日期是回文。解题思路:如果通过枚举两个日期之间的所有日期,然后判断回文,则会有几个问题:枚举数据规模在10^7级别,再加上对于日期加一天、判断回文等处理,有可能超时,而且对日期进行加一天、判断回......
  • CSP历年复赛题-P2672 [NOIP2015 普及组] 推销员
    原题链接:https://www.luogu.com.cn/problem/P2672题意解读:N家住户,每家住户与出入口距离是Si米,推销员每走1米疲劳值+1,向第i家住户推销疲劳值+Ai,推销员推销完原路返回出口,计算在向不同数量X的住户推销时,能达到的最大疲劳值。解题思路:本题是一种贪心选择问题,需要思考出可能的最优......
  • CSP历年复赛题-P2671 [NOIP2015 普及组] 求和
    原题链接:https://www.luogu.com.cn/problem/P2671题意解读:找到所有符合条件的三元组,累加三元组的分数,结果对10007取模。解题思路:仔细读题,并分析数据规模,1~4个数据点可以通过O(n^2)复杂度解决,也就是枚举法。1、枚举法要求x<y<z,y−x=z−y,移项可得x+z=2*y,并且c......
  • 打卡信奥刷题(52)用Scratch图形化工具信奥P7909 [普及组] [CSP-J 2021] 分糖果
    [CSP-J2021]分糖果题目背景红太阳幼儿园的小朋友们开始分糖果啦!题目描述红太阳幼儿园有nnn个小朋友,你是其中之一。保证......
  • 2024年云计算、信号处理与网络技术国际学术会议(ICCCSPNT 2024)
    2024年云计算、信号处理与网络技术国际学术会议(ICCCSPNT2024)2024InternationalAcademicConferenceonCloudComputing,SignalProcessing,andNetworkTechnology(ICCCSPNT2024)会议简介:2024年云计算、信号处理与网络技术国际学术会议(简称ICCCSPNT2024)是一个集结了......
  • P5663 [CSP-J2019] 加工零件
    原题链接题解请仔细读题!!!如果1号工人需要提供原材料,那么代表\(a_i\to1\)存在一条长度为\(L_i\)的路径(可以重复走)由于重复走不会改变路径长度的奇偶性,所以一定存在一条奇偶性相同,且长度小于\(L_i\)的路径,所以只要求从点1出发到各个点奇偶最短路即可code#include<bits/......
  • CSP历年复赛题-P1982 [NOIP2013 普及组] 小朋友的数字
    原题链接:https://www.luogu.com.cn/problem/P1982题意解读:特征值:第i个同学的特征值是1~i中最大子段和,分数:第i个同学分数是前1~i-1个同学的分数+特征值最大值,求最大分数。解题思路:第一步:先计算特征值f[i],f[i]等于1~i中所有数的最大子段和,所以借助最大子段和的DP方法,每次计算以i......