非常容易想到一个 O ( n 3 ) O(n^3) O(n3)的dp,于是做不动了。
考虑一个数变大,这个序列的答案只会更大,所以答案不会超过 9 ∗ l o g n 9*log~n 9∗log n
因此状态互换。
设 f i , j f_{i,j} fi,j表示从i开始,在花费j的代价最远能走到哪里。
枚举一个点p:
f i , j = m a x ( f i , j − 1 , [ f i , j − a [ p ] > = p − 1 ] ∗ f p + 1 , j − a [ p ] ) f_{i,j}=max(f_{i,j-1},[f_{i,j-a[p]}>=p-1]*f_{p+1,j-a[p]}) fi,j=max(fi,j−1,[fi,j−a[p]>=p−1]∗fp+1,j−a[p])
发现瓶颈在a[p],不妨对a[p]相同的一起做,维护前缀max,p在j的前面虽然意义上不合法,但是答案只会更劣,所以不用担心答案会错,这是一个简单的套路。
转移复杂度 O ( n ∗ a n s ) O(n*ans) O(n∗ans),维护前缀max复杂度 O ( n ∗ a n s ∗ 10 ) O(n*ans*10) O(n∗ans∗10),卡卡常数就能过。
实际上有更优的复杂度:
再设 g i , j g_{i,j} gi,j表示从i开始,代价为j,最左能够到哪儿。
这两个东西可以互相转移,也是用维护前后缀max的方式,比较巧妙。
#include
#include
#define pp printf
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 1e5 + 5;
int n, a[N], f[154][N], mx[N];
char s[N];
int main() {
freopen("cost.in", "r", stdin);
freopen("cost.out", "w", stdout);
scanf("%s", s + 1); n = strlen(s + 1);
fo(i, 1, n) a[i] = s[i] - '0';
fo(i, 1, n) f[a[i]][i] = i, f[0][i] = i - 1;
fo(j, 0, 153) f[j][n + 1] = n;
fo(j, 1, 153) {
fo(i, 1, n) f[j][i] = max(f[j][i], f[j - 1][i]);
fo(k, 1, 9) {
if(j < k) break;
fo(i, 1, n) if(a[i] == k)
mx[i] = max(mx[i - 1], f[j - a[i]][i + 1]); else
mx[i] = mx[i - 1];
fo(i, 1, n) mx[i] = max(mx[i], mx[i - 1]);
fo(i, 1, n) f[j][i] = max(f[j][i], mx[f[j - k][i] + 1]);
}
if(f[j][1] >= n) {
printf("%d\n", j); return 0;
}
}
}