【备战蓝桥杯国赛-国赛真题】最大数字
题目链接:https://www.dotcpp.com/oj/problem2694.html
思路
我们的目标通过若干次的两类操作(有次数限制),使得最终的数字尽可能大,而数字的大小与数的位数有关,并且处于高位的数字需要尽可能的高。本题的数据范围是1e17,看起来很大,但是分析之后我们只需要考虑其中的每一位,也只有20不到的数据量,接着我们来分析这两类操作(从高位到低位考虑):
- 加1操作:应该尽可能将数字增加到9,如果已经是9,那么我们贪心考虑的结果是,不需要对当前位进行操作,如果该操作的次数不足以让数字增加到9,那么我们全部操作完即可。
- 减1操作:该操作的目的其实只有一个,就是把该位的数字减到9,因为如果减少不到9,那么数的值将会变小,如果减少到8,那说明一定可以减少到9,并且减少到8或者更小都不如减少到9更优,因此,如果该位减少不到9,那么我们不对该位进行减1操作。
- 说明:可以证明,在最优方案中,这两种操作不会对同一位进行大于0次的操作,为了方便,我们称之为合操作。一个直观的解释是这样的,这两种的操作是互相抵消的,如果合操作存在,那么另任意一种操作数为0后,结果一定也能达到,并且增加了另一种操作的可操作次数,这样不会使得最终结果更糟糕。
接下来就是代码部分了,使用爆搜就可以,2^17的复杂度很友好,如果对下面中的代码有不理解的地方,欢迎大家交流。
代码(C++)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL n, res = 0;
int a, b;
string s;
void dfs(int u, LL v) {
if(u == (int)s.size()) {
res = max(res, v);
return ;
}
LL x = s[u] - '0';
LL t = min((LL)a, 9 - x);
a -= t;
dfs(u + 1, v * 10ll + x + t);
a += t;
if(b >= x + 1) {
b -= x + 1;
dfs(u + 1, v * 10ll + 9ll);
b += x + 1;
}
}
int main() {
cin >> n >> a >> b;
s = to_string(n);
dfs(0, 0ll);
cout << res << "\n";
return 0;
}