D3题目及思路

本文介绍了利用动态规划和滑动窗口解决字符串处理问题的典型例题,包括寻找购买商品最大喜爱值的问题、找出不包含重复字母的最长子串长度以及找到最长回文子串的长度。通过示例代码详细阐述了马拉车算法在回文串查找中的应用,展示了如何高效地解决这类问题。

D3:
T1:买东西
题意:假期到了,小 A 因为期末考试考的好,所以获得了奖金 n 元。现在他来到了
超市,想要买些自己喜欢的东西。超市里有 m 种商品,每一样商品都有他对应
的价值和小 A 对它的喜爱值,且都有无数件,小 A 希望用这些钱买到的东西喜爱值最高。
Input
共两行
第一行两个整数 n m,分别为奖金钱数和超市商品件数。
接下来𝑚行,第𝑖 + 1行两个整数,分别对应第𝑖件商品的价值𝑣𝑖和小 A 的喜爱值𝑎𝑖
Output
一个整数,小 A 买到的物品的最大喜爱值。
Hint
30% 𝑛 ≤ 100, 𝑚 ≤ 10
100% 𝑛 ≤ 1000, 𝑚 ≤ 1000, 0 < 𝑣𝑖 < 100000, 0 < 𝑎𝑖 < N
思路:完全背包板子题
代码:

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int n,m ,v[1010];
long long a[1010],dp[1010];
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i++){
        scanf("%d%lld",&v[i],&a[i]);
    }
    for(int i = 1;i <= m;i++){
        for(int j = v[i];j <= n;j++){
            dp[j] = max(dp[j],dp[j - v[i]] + a[i]);
        }
    }
    printf("%lld\n",dp[n]);
    return 0;
}

T2:滑动窗口
题意:给定一个由小写字母构成的字符串,求不包含重复字母的最长子串长度。子
串的定义为字符串中连续的一部分,如对 abcde 来说,bcd 是子串,但 ace 不
是。如对于 aabcdcda 来说,不包含重复字母的最长子串是 abcd,长度为 4。
Input
一行,为输入的字符串 s。
Output
一个整数,为不包含重复字母的最长子串长度。
Hint
40% s 的长度<=1000
100% s 的长度<=10000000
思路:解法如题目:滑动窗口。
枚举每一位字符,用cnt数组记录字符情况,如果有重复,记录最优解,然后从记录起始字串位置向后枚举,清除cnt记录直到找到与当前字符相同的位置,由此往复,直到字符串枚举完毕。
代码:

#include<cstdio>
#include<iostream> 
using namespace std;
string s;
int cnt[300];
int main(){
	cin >> s;
    int l = s.length(),st = 0,ans = 0;
    for(int i = 0;i < l;i++){
        if(cnt[s[i]]){
            ans = max(ans, i - st);
            while(s[st] != s[i])
                cnt[s[st++]] = 0;
            st++;
        }
        cnt[s[i]] = 1;
    }
    ans = max(l - st,ans);
	printf("%d\n",ans);
	return 0;
}

T3:最长回文字符串
题意:回文字符串的定义是正着写和倒着写一样的字符串,如回文串 abcba 正着
和倒着都是 abcba,而 abcd 倒过来是 dcba,就不是回文字符串。现在给出一个
由字母构成的字符串 s。要求输出这个字符串 s 中最长的回文子串的长度 l。
Input
一行,字符串 s
Output
一行一个整数,最长回文子串的长度 l
Hint
70% 𝑛 ≤ 1000
100% 𝑛 ≤ 1000000
思路:若本题使用暴力算法,一定会超时,因此考虑马拉车算法。
马拉车算法:首先先在每个字符之间插入原字符串中不可能出现的字符(与本题如’#‘,’$‘,’@'等),将所有回文字符串长度变为奇数,然后定义数组p,整数mi和right,p[i]表示以i为中心的回文字符串半径,mi与right分别表示目前右端点最靠右的回文字符串中心和半径
接下来从字符串起始字符开始枚举,设当前枚举位置为i:
当right>i时,如下图:

a # b # c # b #c# b # a #b#c#b#c#

   2mi-i       mi       i   right

那么可以保证i到mi一定与mi到(2mi-i)之间的字符一定对称
但是如果i到right的距离小于p[2 * mi - i],则不能保证right到p[2mi-i]+i之间的字符与2mi-i与p[2mi-i]之间的字符对称,则p[i]等于right-i,反之p[i]等于p[2*mi-i]
当right<=i时,无法保证i到right之间的字符对称,所以p[i]等于1
然后进行暴力扩张,之后记录最大值输出即可
代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxstr = 1000100;
char c[maxstr],a[maxstr * 2 + 10];
int p[maxstr * 2 + 10];
int main(){
    scanf("%s",c + 1);
    a[0] = '$';
    int len = strlen(c + 1);
    for(int i = 1;i <= len;i++){
        a[i * 2] = c[i];
    }
    for(int i = 1;i <= len;i++){
        a[i * 2 - 1] = '#';
    }
    len = len * 2 + 2;
    a[len - 1] = '#';
    a[len] = '\0';
    int mi = 0,right = 0;
    int maxpoint = 0, maxlen = 0;
    for(int i = 1;i < len;i++){
        p[i] = right > i ? min(p[2 * mi - i],right - i) : 1;
        while(a[i + p[i]] == a[i - p[i]])p[i]++;
        if(right < i + p[i]){
            right = i + p[i];
            mi = i;
        }
        if(maxlen < p[i]){
            maxlen = p[i];
            maxpoint = i;
        }
    }
    printf("%d\n",maxlen - 1);
    return 0;
}
当然可以!以下是 **5 道贴近蓝桥杯(省赛难度)的 C 语言真题风格题目**,涵盖枚举、模拟、递归、动态规划和数学思维等高频考点。每道题都包含:问题描述、输入输出样例、解题思路提示 和 完整的 **C 语言代码实现**。 --- ### 🔹 蓝桥杯真题风格题 1:特殊年份(模拟 + 数位操作) #### 📌 问题描述 定义一个“特殊年份”: 设某年份为四位数 `abcd`(如 2023),如果满足: - 第二位数字比第一位大 1, - 最后一位数字比第三位大 1, 则称其为特殊年份。 例如:2023 → `0=2+?` ❌;2021 → `0≠2+1` ❌;2123 → `1==2? no` → 实际是:2→1→2→3 → 第2位(1)=第1位(2)-1?不成立。 正确例子:**2021 不是**,但 **1234 是**(2=1+1, 4=3+1) 给定 5 个年份,统计其中有多少个是“特殊年份”。 #### ✅ 输入输出样例 ``` 输入: 2021 2123 1999 2022 1234 输出: 2 ``` 解释:2123(1=2+1? 否)、等等 → 正确应为:**2123:第二位=1,第一位=2 → 1≠2+1 → 错** 我们重新定义清楚: > 特殊年份条件: > - 千位:a,百位:b,十位:c,个位:d > - 要求:b == a + 1 且 d == c + 1 所以: - 2021: b=0, a=2 → 0≠3 ❌;d=1, c=2 → 1≠3 ❌ - 2123: b=1, a=2 → 1≠3 ❌ - 1212: b=2==1+1 ✔️;d=2==1+1 ✔️ → ✔️ - 1314: 3==1+2? no → 3==1+2? yes → ✔️;4==4? no → 4==1+3? ✔️ → ✔️ 更正样例: ``` 输入: 1212 1314 2021 2043 2022 输出: 3 ``` (1212 ✔️,1314 ✔️,2043 ✔️(0==2+? no)→ 等等! 2043: a=2,b=0 → 0≠3 ❌ 找对的: - 1212 ✔️ - 1314 ✔️ - 2324 ✔️ - 8989 ✔️(9==8+1, 9==8+1) 所以输入示例改为: ``` 输入: 1212 1314 2324 8989 1234 输出: 4 ``` ```c #include <stdio.h> int is_special(int year) { int a = year / 1000; int b = (year / 100) % 10; int c = (year / 10) % 10; int d = year % 10; return (b == a + 1) && (d == c + 1); } int main() { int count = 0; for (int i = 0; i < 5; i++) { int y; scanf("%d", &y); if (is_special(y)) { count++; } } printf("%d\n", count); return 0; } ``` --- ### 🔹 蓝桥杯真题风格题 2:成绩统计(四舍五入百分比) #### 📌 问题描述 小蓝有 `n` 名同学的成绩(百分制整数)。 计算: - 及格率(≥60)% - 优秀率(≥85)% 结果以百分比表示,**四舍五入到整数**。 #### ✅ 输入输出样例 ``` 输入: 5 60 70 80 90 100 输出: 80 40 ``` 解释: - 及格:5人中5人都 ≥60 → 5/5=100%?错!只有 60,70,80,90,100 → 全部及格 → 100% - 优秀:90,100 → 2人 → 2/5=40% 但上面输出是 80 和 40?说明不是这个数据。 改样例: ``` 输入: 6 50 60 70 80 85 95 输出: 83 33 ``` 计算: - 及格:60,70,80,85,95 → 5人 → 5/6 ≈ 83.33% → 四舍五入 = 83 - 优秀:85,95 → 2人 → 2/6 ≈ 33.33% → 33 ```c #include <stdio.h> #include <math.h> int round_double(double x) { return (int)(x + 0.5); } int main() { int n; scanf("%d", &n); int pass = 0, excellent = 0; for (int i = 0; i < n; i++) { int score; scanf("%d", &score); if (score >= 60) pass++; if (score >= 85) excellent++; } double pass_rate = (double)pass / n * 100; double excel_rate = (double)excellent / n * 100; printf("%d\n%d\n", round_double(pass_rate), round_double(excel_rate)); return 0; } ``` > ⚠️ 注意:蓝桥杯评测环境可能无 `math.h` 的 `round()`,建议手动 `(int)(x + 0.5)` --- ### 🔹 蓝桥杯真题风格题 3:回文日期(枚举 + 判断) #### 📌 问题描述 给出一个 8 位数字表示的日期(格式 `YYYYMMDD`,如 20210102),判断它是否是一个“回文日期”(即该数字本身是回文数)。 进一步地,判断它是否同时满足“ABABBABA”型(如 20200202,其中 A=2, B=0)这种特殊回文。 输出两行: - 第一行:如果是回文日期输出 1,否则 0 - 第二行:如果是 ABAB 型回文输出 1,否则 0 #### ✅ 输入输出样例 ``` 输入: 20200202 输出: 1 1 ``` ```c #include <stdio.h> // 判断8位数是否回文 int is_palindrome(long long n) { long long reversed = 0, temp = n; while (temp) { reversed = reversed * 10 + temp % 10; temp /= 10; } return reversed == n; } // 判断是否为 ABAB 型:格式 A B A B B A B A int is_abab_type(int date) { int d[8]; int temp = date; for (int i = 7; i >= 0; i--) { d[i] = temp % 10; temp /= 10; } // 格式:d0 d1 d2 d3 d4 d5 d6 d7 // 要求:d0==d2==d5==d7=A,d1==d3==d4==d6=B return d[0] == d[2] && d[2] == d[5] && d[5] == d[7] && d[1] == d[3] && d[3] == d[4] && d[4] == d[6]; } int main() { long long date; scanf("%lld", &date); printf("%d\n", is_palindrome(date) ? 1 : 0); printf("%d\n", is_abab_type((int)date) ? 1 : 0); return 0; } ``` --- ### 🔹 蓝桥杯真题风格题 4:最大子段和(动态规划基础) #### 📌 问题描述 给定 `n` 个整数(可负),求连续子数组的最大和(至少选一个元素)。 使用 Kadane 算法。 #### ✅ 输入输出样例 ``` 输入: 8 -2 3 -1 4 -5 2 6 -3 输出: 8 ``` 解释:3 -1 +4 = 6;2+6=8;最大是 2+6=8?或者 3-1+4=6? 实际:`3 -1 4` → 6;`2 6` → 8;最大是 8 ```c #include <stdio.h> #include <limits.h> int max(int a, int b) { return a > b ? a : b; } int main() { int n; scanf("%d", &n); int arr[n]; for (int i = 0; i < n; i++) { scanf("%d", &arr[i]); } int max_sum = INT_MIN; int current_sum = 0; for (int i = 0; i < n; i++) { current_sum = max(arr[i], current_sum + arr[i]); max_sum = max(max_sum, current_sum); } printf("%d\n", max_sum); return 0; } ``` --- ### 🔹 蓝桥杯真题风格题 5:蛇形填数(规律模拟) #### 📌 问题描述 如下图所示方式填充一个无限矩阵: ``` 1 2 6 7 15 ... ↗ ↙ ↗ 3 5 8 14 ↗ ↙ ↗ 4 9 13 ↙ ↗ 10 12 ↗ 11 ``` 问:数字 **2020** 出现在第几行第几列? > 实际上这是按“反对角线”方向来回填数。 我们可以发现: - 第 k 条对角线(从左上到右下)有 k 个元素 - 如果 k 是奇数:从下往上填(行递减) - 如果 k 是偶数:从上往下填(行递增) 我们要找的是:哪个位置的值等于 2020 #### 解法:模拟填数直到达到 2020 由于 2020 较小,可以用二维数组模拟前几百行。 ```c #include <stdio.h> #include <string.h> #define MAX 1000 int grid[MAX][MAX]; int main() { int n = 2020; int num = 1; int k = 1; while (num <= n) { if (k % 2 == 1) { // 奇数层:从下往上 → 行从 k-1 到 0 for (int i = k - 1; i >= 0; i--) { int j = k - 1 - i; if (i < MAX && j < MAX) { grid[i][j] = num++; } } } else { // 偶数层:从上往下 for (int i = 0; i < k; i++) { int j = k - 1 - i; if (i < MAX && j < MAX) { grid[i][j] = num++; } } } k++; } // 查找 2020 所在行列(从1开始计数) for (int i = 0; i < MAX; i++) { for (int j = 0; j < MAX; j++) { if (grid[i][j] == 2020) { printf("Row: %d, Col: %d\n", i + 1, j + 1); return 0; } } } return 0; } ``` > 注:此题也可用数学公式推导,但模拟法适合蓝桥杯编程题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值