[原题地址](8.最大数字 - 蓝桥云课 (lanqiao.cn))
#错误求解
第一次求解时,完全按照题目的介绍方式暴力求解,从头到尾的遍历了操作1和操作2,导致时间复杂远超出题目要求。
#include <iostream>
#include <vector>
#include<algorithm>
#include<cmath>
using namespace std;
int re=0;
vector<int> ve;
int sum = 0;
int f1(int n){
if(n>=0 && n<9) n++;
else if(n == 9) n=0;
return n;
}
int f2(int n){
if(n>0 && n<=9) n--;
else if(n == 0) n=9;
return n;
}
"""该方法时间复杂度过大,不断在递归中调用的暴力解法,"""
"""当N,A,B偏大时运行时间过长"""
bool dfs(int a,int b){
if(b == 0){
for(int j=0;j<ve.size();j++){
if(a != 0){
a--;
ve[j] = f1(ve[j]);
dfs(a,b);
a++;
ve[j] = f2(ve[j]);
}
}
}
if(a == 0){
for(int k=0;k<ve.size();k++){
sum += ve[k]*pow(10,k);
//cout<<sum<<"\n"; //在求和的过程中,在最后一步会自动将最后一位减1
// 猜测是由于二进制的原因?
}
//cout<<sum<<" "<<re<<"\n";
if(sum > re){
re = sum;
}
sum = 0;
return true;
}
for(int i=0;i<ve.size();i++){
if(b != 0){
b--; //使用一次f2之后剩余的机会
ve[i] = f2(ve[i]);
dfs(a,b);
b++;
ve[i] = f1(ve[i]);
}
}
}
int main()
{
int N,A,B;
cin>>N>>A>>B;
//vector<int> ve;
while(N!=0){
ve.push_back(N%10);
N = N/10;
}
dfs(A,B);
cout<<re+1;
return 0;
}
#分析原因 + 题目分析
1.首先使用int类型作为输入数和输出数的存储类型,超出了该类型所能存储的最大值。对于题目,应采用long long 类型。
int的存储大小:4字节的8位可表示数,2^32=42 9496 7296
long long的存储大小:8字节8位可表达位数,2^64=1844 6744 0737 0960 0000
2.在上述代码中,将每一位数字都进行了操作1和操作2,递归条件是当操作1和操作2和剩余数都为0。实际上进行操作1后,不再对该位的数字进行操作2,不然相当于浪费空间,同时题目并未要求要将操作1和操作2都使用完。
3.题目希望得出最大值,即保障从高位到低位每一位数字尽可能的大。
4.操作1使用加法,我们希望通过操作1将该位的数字提高到尽可能大。此时通过操作1,会有两种可能(设该位数字位x,操作1的数量还有n1个):
(1)n1>=9-x:可以将该位提至9;
(2)n1<9-x :该位最高提至x+n1;
所以,从高到低遍历的过程中,取n1和9-x中的最大值作为该位要增加的值。
5.操作2使用减法,我们只在该位数字可能由0转1时使用,(设该位数字位x,操作2的数量还有n2个)即当n2>x时使用。
6.优先使用操作1,然后进行递归,之后如果有进行操作2的条件就使用操作2。
7.注意,在递归前后修改n1和n2的值。
#正确代码如下
#include<iostream>
using namespace std;
typedef long long LL;
char c[20];
LL ans;
int n1,n2; //f1,f2分别为剩余次数
void dfs(int i,LL sum){
int x = c[i] - '0';
if(c[i]){
int t = min(n1,9-x);
n1 -= t;
dfs(i+1,sum*10+x+t);
n1 += t;
if(n2>x){
n2 -= x+1;
dfs(i+1,sum*10+9);
n2 += x+1;
}
}
else{
ans = max(ans,sum);
}
}
int main(){
cin>>c>>n1>>n2;
dfs(0,0) ;
cout<<ans;
return 0;
}