蓝桥杯31天真题冲刺|题解报告|第二十六天

大家好,我是snippet,今天是刷蓝桥真题的第二十六天,今天的知识点包含搜索+动态规划,下面是我今天的题解

目录

一、玩具

题目链接:B-玩具_牛客小白月赛69 (nowcoder.com)

题目内容:

解题思路:

代码:

二、滑雪

题目链接:滑雪_牛客题霸_牛客网 (nowcoder.com)

题目内容:

解题思路:

代码:

三、abb

题目链接:abb_牛客题霸_牛客网 (nowcoder.com)

题目内容:

解题思路:

代码:

四、小红取数 

题目链接:小红取数_牛客题霸_牛客网 (nowcoder.com)

题目内容:

解题思路:

代码:


一、玩具

题目链接:B-玩具_牛客小白月赛69 (nowcoder.com)

题目内容:

题目描述:

有 n 个玩具,第 i 个玩具的价格是 ai​ 元,超市里搞促销活动,购买 2 个玩具即可免单其中价格较低的一个,价格相等也免单其中一个。牛牛想买下所有玩具,至少需要花多少元?

输入描述:

第一行一个正整数 n(1≤n≤10^6)。

第二行 n 个正整数,第 i 个表示 ai​(1≤ai​≤10^9)。

输出描述:

输出一行一个正整数,表示答案。

示例1

输入

3

1 2 3

输出

4

说明

第二个和第三个一起买,花 3 元,再花 1 元买下第一个,合计 4 元。

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

解题思路:

因为买玩具可以买一送一嘛,买贵的送便宜的 那我们就先从所有玩具中找到最贵的,买它 然后送只比它便宜的另外一个玩具 重复进行这个操作,就可以用最少的钱买这所有的玩具了

代码:

package 蓝桥杯31天真题冲刺.Day26;

import java.io.*;
import java.util.Arrays;

/**
 * @author snippet
 * @data 2023-03-29
 * 玩具-牛客网
 */
public class T1_玩具 {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

    static int n;
    static long sum;
    static int N = 1000010;
    static long[] arr = new long[N];

    public static void main(String[] args) throws IOException {
        String[] s = br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        s = br.readLine().split(" ");
        for (int i = 0; i < n; i++) {
            arr[i] = Long.parseLong(s[i]);
        }
        Arrays.sort(arr, 0, n);

        for (int i = n-1; i >= 0; i-=2) {
            sum += arr[i];
        }

        pw.println(sum);
        pw.flush();
        br.close();
    }
}

二、滑雪

题目链接:滑雪_牛客题霸_牛客网 (nowcoder.com)

题目内容:

题目描述:

给定一个 n×m  的矩阵,矩阵中的数字表示滑雪场各个区域的高度,你可以选择从任意一个区域出发,并滑向任意一个周边的高度严格更低的区域(周边的定义是上下左右相邻的区域)。请问整个滑雪场中最长的滑道有多长?(滑道的定义是从一个点出发的一条高度递减的路线)。

(本题和矩阵最长递增路径类似,该题是当年NOIP的一道经典题)


数据范围: 1≤n,m≤100 ,矩阵中的数字满足 1≤val≤1000 

输入描述:

第一行输入两个正整数 n 和 m 表示矩阵的长宽。
后续 n 行输入中每行有 m 个正整数,表示矩阵的各个元素大小。

输出描述:

输出最长递减路线。

示例1

输入:

5 5

1 2 3 4 5

16 17 18 19 6

15 24 25 20 7

14 23 22 21 8

13 12 11 10 9

输出:

25

说明:

从25出发,每次滑向周边比当前小 1 的区域。 25->24->23->22->......->1

解题思路:

找到最长的递减的滑雪路径,我们可以使用dfs来对每个位置进行记忆化遍历搜索,如果它周围有高度比它低的区域,就对满足条件的区域进行递归遍历,每一条路递归遍历之后记得要回溯

代码:

package 蓝桥杯31天真题冲刺.Day26;

import java.io.*;

/**
 * @author snippet
 * @data 2023-03-29
 * DP18_滑雪-牛客网
 */
// 记忆化搜索
public class T2_DP18_滑雪 {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

    static int n,m,ans;
    static int N = 101;
    static int[][] arr = new int[N][N];
    static boolean[][] visit = new boolean[N][N];
    static int[] dx = {0, 0, 1, -1};
    static int[] dy = {1, -1, 0, 0};

    // 记忆化搜索
    static void dfs(int x, int y, int cnt) {
        // 每次遍历一个位置的时候都进行一次答案更新
        ans = Math.max(ans, cnt);
        // 标记遍历的位置
        visit[x][y] = true;
        for (int i = 0; i < 4; i++) {
            int x1 = x + dx[i];
            int y1 = y + dy[i];
            if (x1 > 0 && x1 <= n && y1 > 0 && y1 <= m && arr[x][y] > arr[x1][y1] && !visit[x1][y1]) {
                // 递归
                dfs(x1, y1, cnt+1);
            }
        }
        // 回溯
        visit[x][y] = false;
    }

    public static void main(String[] args) throws IOException {
        String[] s = br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        m = Integer.parseInt(s[1]);
        for (int i = 1; i <= n; i++) {
            s = br.readLine().split(" ");
            for (int j = 1; j <= m; j++) {
                arr[i][j] = Integer.parseInt(s[j-1]);
            }
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                dfs(i, j, 1);
            }
        }
        pw.println(ans);
        pw.flush();
        br.close();
    }
}

三、abb

题目链接:abb_牛客题霸_牛客网 (nowcoder.com)

题目内容:

题目描述

leafee 最近爱上了 abb 型语句,比如“叠词词”、“恶心心”

leafee 拿到了一个只含有小写字母的字符串,她想知道有多少个 "abb" 型的子序列?
定义: abb 型字符串满足以下条件:

1.字符串长度为 3 。

2.字符串后两位相同。

3.字符串前两位不同。

输入描述:

第一行一个正整数 n

第二行一个长度为 n 的字符串(只包含小写字母)

1≤n≤10^5

输出描述:

"abb" 型的子序列个数。

示例1

输入:

6

abcbcc

输出:

8

说明:

共有1个abb,3个acc,4个bcc

示例2

输入:

4

abbb

输出:

3

解题思路:

因为是求给定的字符串中只根据前后顺序来排子串,满足abb型的子串的个数(如果a,b,b字符一样,但是在字符串中的位置不一样,按不同种进行计算)

我最开始想到的是直接三层for循环进行遍历,因为数据范围是1≤n≤10^5,肯定会超时,没想到居然过了72.73%

根据题意,我们可以使用后缀和来求解,因为是求abb的串的个数,那我们就可以先给定a,然后求a后面的b的个数,那我们可以求每个位置数的后面字符的每个字符(a-z)的字符数和,然后我们对每个数进行遍历,再根据它的后缀和中每个字符的个数k来进行答案求和, ans += ki * (ki-1) / 2  (i=> [a, z]);(ka表示这个字符包括自己 后面的字符中含有a的个数)

代码:

package 蓝桥杯31天真题冲刺.Day26;

import java.io.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @author snippet
 * @data 2023-03-29
 * abb-牛客网
 */
public class T3_abb {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

    static int n;
    static long ans;
    static int N = 100010;
    static char[] c;
    static int[][] h = new int[N][26];// 二维数组h表示每个i后面的所有字母a-b的个数

    public static void main(String[] args) throws IOException {
        String[] s = br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        s = br.readLine().split(" ");
        c = s[0].toCharArray();
        int len = c.length;
        // 求后缀和
        // 列表示每个字母 行0-26表示这个字母以及后面的字母a-z的数的个数 存的数表示
        for (int i = len-1; i >= 0; i--) {
            char a = c[i];
            for (int j = 0; j < 26; j++) {
                h[i][j] = h[i+1][j];
            }
            // 把自己加上
            h[i][a-'a']++;
        }

        for (int i = 0; i < len; i++) {
            char a = c[i];
            for (int j = 0; j < 26; j++) {
                if (a - 'a' != j) {
                    ans += (long) h[i][j] * (h[i][j]-1) / 2;
                }
            }
        }
        pw.println(ans);
        pw.flush();
        br.close();
    }

//    // for循环 暴力求解 案例通过率:72.73%
//    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
//
//    static int n,ans;
//    static char[] c;
//
//    public static void main(String[] args) throws IOException {
//        String[] s = br.readLine().split(" ");
//        n = Integer.parseInt(s[0]);
//        s = br.readLine().split(" ");
//        c = s[0].toCharArray();
//        int len = c.length;
//        for (int i = 0; i < len; i++) {
//            char a = c[i];
//            for (int j = i+1; j < len; j++) {
//                char b = c[j];
//                if (a != b) {
//                    for (int k = j+1; k < len; k++) {
//                        char d = c[k];
//                        if (a != b && b == d) ans++;
//                    }
//                }
//
//            }
//        }
//        pw.println(ans);
//        pw.flush();
//        br.close();
//    }
}

四、小红取数 

题目链接:小红取数_牛客题霸_牛客网 (nowcoder.com)

题目内容:

题目描述:

小红拿到了一个数组,她想取一些数使得取的数之和尽可能大,但要求这个和必须是  k  的倍数。
你能帮帮她吗?

输入描述:

第一行输入两个正整数  n  和  k 

第二行输入  n  个正整数  ai​ 

1≤n,k≤10^3

1≤ai​≤10^10

输出描述:

如果没有合法方案,输出 -1。
否则输出最大的和。

示例1

输入:

5 5

4 8 2 9 1

复制输出:

20

说明:

取后四个数即可

解题思路:

因为我们要求给定的n个数中,任意取1至n个相加,组成的数x满足x%k == 0,求x的最大值,

那我们就可以使用二维dp对i个数中对k取余得到的每种情况进行状态转移,二维数组f[i][j]表示 每个数i与前面i个数中对k取模的值为j的最大值,f[n][0]也就是我们要求的最大的和

状态转移式:f[i][(j+arr[i])%k] = max(f[i-1][j] + arr[i], f[i-1][(j+arr[i])%k]);

代码:

package 蓝桥杯31天真题冲刺.Day26;

import java.io.*;
import java.util.Arrays;

/**
 * @author snippet
 * @data 2023-03-29
 * 小红取数-牛客网
 */
// 动态规划 二维dp
public class T4_DP40_小红取数 {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

    static int n,k;
    static int N = 1010;
    static long[] arr = new long[N];
    static long[][] f = new long[N][N];// 二维数组f 行i表示前i个数 列表示这前i个数中%k的余数为j的最大值

    public static void main(String[] args) throws IOException {
        String[] s = br.readLine().split(" ");
        n = Integer.parseInt(s[0]);
        k = Integer.parseInt(s[1]);
        s = br.readLine().split(" ");
        for (int i = 1; i <= n; i++) {
            arr[i] = Long.parseLong(s[i-1]);
        }

        // 数据初始化
        for (int i = 0; i <= n; i++) {
            Arrays.fill(f[i], Long.MIN_VALUE);
        }

        // 状态初始化 0个数
        f[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < k; j++) {
                // 状态转移
                f[i][(int) ((j+arr[i])%k)] = Math.max(f[i-1][j]+arr[i], f[i-1][(int) ((j+arr[i])%k)]);
            }
        }
        // 如果没有合法方案就输出-1
        pw.println(f[n][0] > 0 ? f[n][0] : -1);
        pw.flush();
        br.close();
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值