题解:P11386 [POI 2024/2025 R1] Zamek cykliczny
先让我们考虑一个贪心。
想要得到
那么对于给定的
执行上述操作直到
这个贪心可以通过子任务
这个贪心显然是错的,比如当
问题在于我们并不能够确定位移操作的执行次数,在某些情况下使用增加操作去代替位移操作可能是更优的。
假设要进行
例如在
我们的策略是,先对后缀
之后再按照最开始的贪心来处理前缀
同时先进行位移操作再将后缀
令
可以发现,对后缀
贪心最多只会耗费
令
还存在一种情况就是存在一种后缀形如
所以总共需要考虑的情况只有
#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;
}