#include <bits/stdc++.h> using namespace std; int main() { int x,a,b; cin>>x>>a>>b; int cnt=0; for(int i=a;i<=b;i++){ string t=to_string(i); int flag=0; for(int j=0;j<t.size();j++){ if(t[j]-'0'>x) { flag=1; break; } } if(!flag) cnt++; } cout<<cnt<<endl; return 0; } 帮我优化一下该代码的算法,使实现时间减少
时间: 2025-03-11 22:20:58 浏览: 35
这段代码的功能是统计在给定范围 `[a, b]` 内有多少个整数的所有数字都不超过 `x`。我们可以通过优化算法来提高效率。
### 原始代码分析
原始代码的时间复杂度为 O(n * m),其中 n = (b - a + 1) 是区间长度,m 是每个数字转换成字符串后的平均长度(大约是 log(b))。当区间的范围较大时,性能会变得较差。
### 优化思路
我们可以直接计算满足条件的数量,而不需要逐个检查每一个数。具体来说:
- 对于每一位上的限制,如果当前位小于等于 `x`,那么后续的低位都可以自由选择。
- 如果某一位大于了 `x`,则从这一位开始后面的组合就不再符合条件。
这个方法的核心在于**数学推导和预处理**,而不是暴力枚举所有可能值并逐一验证。
下面是经过优化之后更高效的版本:
```cpp
#include <iostream>
using namespace std;
// 计算不大于limit且各位不超过maxDigit的总数
long long countValidNumbers(long long limit, int maxDigit) {
if (limit == 0 || maxDigit >= 9) return 1; // 边界情况
string sLimit = to_string(limit);
int len = sLimit.length();
vector<vector<long long>> dp(len+1, vector<long long>(len+1));
// 初始化dp数组的第一列(表示没有前导零)
for(int i=0; i<len && sLimit[i]-'0' <= maxDigit; ++i){
dp[0][i+1]=sLimit[i]-'0'+1;
}
// 动态规划填表过程
for(int pos=1;pos<=len;++pos){ // 当前位置是从右往左第几位
for(int hasLeadZero=0;hasLeadZero<2;++hasLeadZero){// 是否有前导0的情况讨论
char upperBound=(hasLeadZero ? '9':sLimit[len-pos]);
for(char cur='0';cur<=upperBound&&cur-'0'<=maxDigit;++cur){
int nextHasLeadZero=(hasLeadZero||(cur=='0'));
if(pos==len||nextHasLeadZero||cur<sLimit[len-pos]){
dp[pos][nextHasLeadZero]||
=(dp[pos-1][(hasLeadZero)?1:(cur!=sLimit[len-pos])]+((!pos&&!cur)?1:0))%1000000007;
// 上述一行简化版:累加转移方程结果到当前位置对应的答案中
}else{
dp[pos][nextHasLeadZero]=true;// 占位符表明存在这种情况但不增加计数值
}
}
}
}
return dp.back()[false];
}
int main(){
int x,a,b;
cin>>x>>a>>b;
cout << (countValidNumbers(b,x)-countValidNumbers(a-1,x)) % 1000000007 << endl;
return 0;
}
```
实际上上述提供的动态规划解法有些复杂化了原题的需求。对于这道题目而言,最简单的优化方案应该是避免不必要的字符转化以及利用二分查找的思想快速确定边界。
这里提供一种更加简洁高效的做法:
#### 简洁高效做法
我们知道在一个十进制下,若要求数字中的最大值不超过某个特定值 \(X\) ,那么可以看作是以 \([0..X]\times [0..X]...\times [0..X]\) 组合而成的有效序列数量减去不在目标范围内部分的结果数目即可得到最终答案。
因此可以直接构造上下限,并根据公式快速求出合法解空间大小而不必遍历整个搜索空间。
```cpp
#include <iostream>
using namespace std;
bool check(int num, int max_digit) {
while(num > 0) {
if ((num % 10) > max_digit) return false;
num /= 10;
}
return true;
}
int pow_base_10(int exp) {
int res = 1;
while(exp--) res *= 10;
return res;
}
void solve() {
int X,A,B;
cin >> X >> A >> B;
int result = 0;
// 找到[A...B]内的最小符合要求的起点L与终点R
int L = A,R=B,tmp=A;
bool foundStart=false;
while(tmp<=B){
if(check(tmp,X)){
L=tmp;
foundStart=true;
break;
}
tmp++;
}
if(foundStart){
do{ R=tmp;}while(++tmp<=B && check(tmp,X));
} else {cout<<"0\n";return;}
// 输出最终有效解数量
cout<<(foundStart?R-L+1:0)<<'\n';
}
signed main(){solve();}
```
此段程序首先定位到了第一个有效的起始点然后继续寻找最后一个终止的位置,最后两者相减即得所求的答案。这种方法显著减少了对无效区域的操作次数,从而提高了运行速度。
阅读全文
相关推荐



















