数数 蓝桥杯


image-20220125180806503


解题思路

这个题比较水,输入数据 n 的取值范围为1~10,范围很少,可以直接用switch选择结构根据n的值输出结果。只需要我们事先计算出结果即可,这个结果也不难求,很快就算出来了,计算过程就不讲解了。

代码

#include<iostream>
using namespace std;
int main(){
    int n;
    cin >> n;
    switch (n)
    {
    case 1:
    case 9:
    case 10:
        cout << 1 << endl;
        break;
    case 2:
    case 3:
    case 7:
    case 8:
        cout << 2 << endl;
        break;
    case 4:
    case 5:
    case 6:
        cout << 3 << endl;
        break;
    }
    return 0;
}
<think> 我们参考了引用[1]中关于Windy数的描述,题目要求不含前导零,例如15是Windy数,但015就不是。解题方法使用数位DP,利用前缀和思想将区间问题转化为两个前缀区间相减。同时,分类填数,从高位到低位枚举,注意不含前导零的限制,最高位只能填1到a_n,其他位0到a_i,并分左分支(0到a_i-1)和填a_i两种情况。 引用[4]中提到了蓝桥杯备赛中的数位DP,这与Windy数问题相关。 用户需求明确为查找蓝桥杯竞赛中Windy数相关的题目、解题思路和代码实现。因此,我们重点根据引用[1]和[4]来组织回答。 解题思路: 1. 问题转化:求区间[L, R]内的Windy数个数,转化为求[0, R]的个数减去[0, L-1]的个数。 2. 数位DP设计: - 状态设计:设dp[pos][pre]表示当前处理到第pos位,前一位数字为pre时的Windy数个数(需满足相邻两位绝对值差至少为2)。 - 注意前导零的处理:因为不含前导零,最高位从1开始。 - 状态转移:枚举当前位数字,若当前位是前导零(即pre为特殊标记,例如-1)则当前位可以选0(继续前导零)或1到上界;若非前导零,则当前位数字与pre的绝对值差至少为2。 3. 记忆化搜索:使用记忆化搜索实现数位DP,参数包括当前位置、前一位数字、是否当前位受上界限制、是否当前位是前导零的状态。 代码实现(以C++为例): ```cpp #include <iostream> #include <cstring> #include <cmath> using namespace std; int dp[15][15]; // dp[pos][pre] 当前位置pos,前一数字为pre int digit[15]; // 存储每一位的上界 // pos:当前位置, pre:前一位的数字(若前面都是前导0则pre设为-1), limit:当前位是否有上界限制, zero:前面是否都是0(前导0状态) int dfs(int pos, int pre, bool limit, bool zero) { if (pos == 0) return 1; // 已经填完所有位,成功计数1(因为前面保证每一位都满足条件,所以最后就是一个Windy数) if (!limit && !zero && dp[pos][pre] != -1) return dp[pos][pre]; // 记忆化:没有限制且不是前导0状态且已经计算过 int up = limit ? digit[pos] : 9; // 当前位的上界 int ans = 0; for (int i = 0; i <= up; i++) { if (zero) { // 前面都是前导0 if (i == 0) ans += dfs(pos-1, -1, limit && (i == up), true); // 继续前导0 else ans += dfs(pos-1, i, limit && (i == up), false); // 不再前导0,pre设为当前数字 } else { if (abs(i - pre) >= 2) // 非前导0状态要求相邻数字差至少为2 ans += dfs(pos-1, i, limit && (i == up), false); } } if (!limit && !zero) dp[pos][pre] = ans; // 记忆化存储 return ans; } int solve(int x) { if (x <= 0) return 0; int len = 0; while (x) { digit[++len] = x % 10; x /= 10; } memset(dp, -1, sizeof(dp)); return dfs(len, -1, true, true); // 起始位置,前一个数字没有(用-1表示),有限制,是前导0状态 } int main() { int L, R; cin >> L >> R; cout << solve(R) - solve(L-1) << endl; return 0; } ``` 注意:Windy数的定义是相邻两个数字之差至少为2的正整数。我们的代码中,dfs在pos=0时返回1,表示一个有效数字(因为过程中已经保证了每一位与上一位的差满足条件,并且不含前导0)。另外,当整个数全0的情况(即0)不被计入,因为题目要求正整数(或者注意题目中不含前导0,所以0不会被计入,但题目一般会说明范围,通常从1开始,所以solve(0)我们返回0,因为x<=0时返回0)。 相关问题: 1. 数位DP中如何处理前导零? 2. Windy数问题还有哪些优化方法? 3. 蓝桥杯中还有哪些常见的数位DP题目? </think> ### 蓝桥杯 Windy数题目及解题方法 #### 题目描述(以P2657 [SCOI2009] Windy数为例) 计算区间 $[L, R]$ 中相邻数字差绝对值至少为 $2$ 的正整数个数(不含前导零)。例如:15是Windy数($|1-5|=4\geq2$),但105不是($|1-0|=1<2$)[^1]。 --- ### 解题思路(数位DP法) #### 1. **前缀和转化** - 求 $[L, R]$ 的Windy数个数 $\Rightarrow$ 计算 $[0, R]$ 的个数减去 $[0, L-1]$ 的个数: $$ \text{ans} = \text{solve}(R) - \text{solve}(L-1) $$ #### 2. **状态设计** 设 $dp[pos][pre]$ 表示: - $pos$:当前处理到数字的第几位(从高位到低位) - $pre$:上一位填的数字(若无前驱则设为 $-1$) - 状态值:从当前位开始构造合法Windy数的方案数[^1][^4] #### 3. **关键处理要点** - **前导零处理**:最高位只能填 $1$ 至上限(如 $123$ 的最高位不能填 $0$)[^1] - **相邻数字约束**:当前位 $i$ 与上一位 $pre$ 需满足 $|i-pre|\geq2$ - **分支策略**: - 左分支:填 $0$ 到 $a_i-1$(无上限限制) - 右分支:填 $a_i$(需继续受上限约束) --- ### 代码实现(C++) ```cpp #include <iostream> #include <cstring> #include <cmath> using namespace std; int dp[15][10]; // dp[pos][pre] int digit[15]; // 存储数字每位的值 // pos:当前位置, pre:前一位数字, limit:是否受上限约束, zero:是否仍是前导零 int dfs(int pos, int pre, bool limit, bool zero) { if (pos == 0) return 1; // 所有位填完,生成一个有效数 if (!limit && !zero && dp[pos][pre] != -1) return dp[pos][pre]; // 记忆化 int up = limit ? digit[pos] : 9; // 当前位上限 int ans = 0; for (int i = 0; i <= up; i++) { if (abs(i - pre) < 2 && !zero) continue; // 非前导零时检查相邻差 bool new_zero = (zero && i == 0); // 维持前导零状态 int new_pre = new_zero ? -1 : i; // 前导零时pre无效 ans += dfs(pos - 1, new_pre, limit && (i == up), new_zero); } if (!limit && !zero) dp[pos][pre] = ans; // 记忆化存储 return ans; } int solve(int x) { int len = 0; while (x) { digit[++len] = x % 10; x /= 10; } memset(dp, -1, sizeof(dp)); return dfs(len, -1, true, true); // 从最高位开始 } int main() { int L, R; cin >> L >> R; cout << solve(R) - solve(L - 1) << endl; return 0; } ``` #### 代码说明: 1. **记忆化搜索**:避免重复计算子问题 2. **状态转移**: - 前导零状态(`zero=true`)时忽略相邻数字差约束 - 非前导零时强制要求 $|i-pre|\geq2$ 3. **复杂度**:$O(\log R \times 10 \times 10)$,可处理 $R \leq 10^9$ --- ### 典型例题 1. **蓝桥杯真题变种** 给定区间 $[1, 10^6]$,求特殊Windy数(需额外满足数位和条件)[^4]。 2. **扩展应用** 将Windy数约束改为 $|i-pre|\geq k$($k$ 可变),需调整状态转移条件[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值