1 条题解
-
0
自动搬运
来自洛谷,原作者为

ddxrS_loves_zxr
一个诗意的个性签名搬运于
2025-08-24 23:06:54,当前版本为作者最后更新于2025-02-05 01:12:44,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
先让我们考虑一个贪心。
想要得到 ,就只能是通过将 循环右移一次,而得到 只能是将 或者原本的 就是这个样子。
那么对于给定的 ,若其最低位 ,那么增加 次,然后位移;若 就直接位移。
执行上述操作直到 全部变为 ,然后再花费两次变成 。
这个贪心可以通过子任务 。
这个贪心显然是错的,比如当 时,进行 次增加操作要优于 次位移操作。
问题在于我们并不能够确定位移操作的执行次数,在某些情况下使用增加操作去代替位移操作可能是更优的。
假设要进行 次位移操作,我们将 分成两段:一段包含前 个非零数位以及在其之后接上的 ;另一段包含剩下的数位。分别记作前缀 和后缀 。
例如在 和 时 。
我们的策略是,先对后缀 进行增加操作,直到后缀全变成 ,需要做 次。
之后再按照最开始的贪心来处理前缀 ,这部分要 次。
同时先进行位移操作再将后缀 全部变为 是不优的,因为你进行位移操作过后,将 变为 的代价会增加 倍。
令 的长度为 ,那么时间复杂度为 。
可以发现,对后缀 进行很多次增加操作是不会优于直接贪心的。
贪心最多只会耗费 次操作,因此我们只需要枚举使得后缀 增加的次数不超过 次的情况。
令 表示 中非零数位的数量,那么只需要 。
还存在一种情况就是存在一种后缀形如 ,这种后缀的增加次数也不会超过 。
所以总共需要考虑的情况只有 种,时间复杂度 。
#include<bits/stdc++.h> typedef long long ll; using namespace std; const int N = 1e6 + 5; int n, k; string s; ll solve(int r) { ll sum = r, i, j, tot = 0, tmp = 0; for(i = 1, j = 0; i <= n && j < r; i++) if(s[i] > '0') sum += '9' - s[i], j++; while(i <= n && s[i] == '0') i++; while(i <= n && s[i] == '9') i++; if(n - i + 1 > 7) return 0x3f3f3f3f3f3f3f3f; for(; i <= n; i++) tot = tot * 10 + 9, tmp = tmp * 10 + s[i] - '0'; return sum + tot - tmp; } int main() { #ifdef ddxrS freopen("sample.in", "r", stdin); freopen("sample.out", "w", stdout); #endif ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin>>s; n = s.length(), s = '#' + s; for(int i = 1; i <= n; i++) k += (s[i] != '0'); if(k == 1 && s[1] == '1' && n == 1) return cout<<0<<'\n', 0; if(k == 1 && s[1] == '1') return cout<<1<<'\n', 0; ll ans = 0x3f3f3f3f3f3f3f3f, lst = n; for(int i = n - 7; i >= 0; i--) if(s[i] != '9') {lst = i; break;} for(int i = max(0, k - 7); i <= k; i++) ans = min(ans, solve(i)); cout<<min(ans, solve(lst)) + 2<<'\n'; return 0; }
- 1
信息
- ID
- 11076
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- (无)
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者