【WC2019模拟2019.1.12】二分的代价(状态互换dp)

版权声明:博主写博客也挺不容易,转载当然阔以,记得先吱一声~ https://blog.csdn.net/Cold_Chair/article/details/86499908

Description:

在这里插入图片描述

题解:

非常容易想到一个 O ( n 3 ) O(n^3) 的dp,于是做不动了。

考虑一个数变大,这个序列的答案只会更大,所以答案不会超过 9 l o g   n 9*log~n

因此状态互换。

f i , j f_{i,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]})

发现瓶颈在a[p],不妨对a[p]相同的一起做,维护前缀max,p在j的前面虽然意义上不合法,但是答案只会更劣,所以不用担心答案会错,这是一个简单的套路。

转移复杂度 O ( n a n s ) O(n*ans) ,维护前缀max复杂度 O ( n a n s 10 ) O(n*ans*10) ,卡卡常数就能过。

实际上有更优的复杂度:
再设 g i , j g_{i,j} 表示从i开始,代价为j,最左能够到哪儿。

这两个东西可以互相转移,也是用维护前后缀max的方式,比较巧妙。

#include<cstdio>
#include<cstring>
#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; 
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Cold_Chair/article/details/86499908