大家好,我是snippet,今天是刷蓝桥真题的第二十六天,今天的知识点包含搜索+动态规划,下面是我今天的题解
目录
一、玩具
题目链接:B-玩具_牛客小白月赛69 (nowcoder.com)
题目内容:
解题思路:
代码:
二、滑雪
题目链接:滑雪_牛客题霸_牛客网 (nowcoder.com)
题目内容:
解题思路:
代码:
三、abb
题目链接:abb_牛客题霸_牛客网 (nowcoder.com)
题目内容:
解题思路:
代码:
四、小红取数
题目链接:小红取数_牛客题霸_牛客网 (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();
}
}
题目描述:
给定一个 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();
}
}
题目描述
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();
// }
}
题目描述:
小红拿到了一个数组,她想取一些数使得取的数之和尽可能大,但要求这个和必须是 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();
}
}