题意是给你一个序列,找出一个连续的划分,使得每部分和小于M ,然后每一部分贡献出最大的值作为该部分的权值,求权值和的最小值。
数据大小是100000
dp方程很容易想到:
dp[i] = min(dp[j]+ max[j+1,i]); j<i;
然而这个时间复杂度是n2的。一般情况是要用一种数据结构来优化,首先容易想到的就是线段树,然而i变化的时候不能保证原来的最大值不变。然后考虑能不能优化状态。
对于现在的状态dp[now] ,
设 i>j , 则此时i和j对应的转化式子为
dp[i]+max[i+1,now] ;
dp[j]+max[j+1,now] ;
由于有dp[i]>=dp[j] (显而易见)具有单调性;
此时max[i+1,now] <= max[j+1,now];
当max[i+1,now] == max[j+1,now]时那么此时的状态就是无效状态。
令j=i-1 得到若 a[i]>a[i-1] ,则max[i+1,now] = max[i,now];
推广得,若存在 a[i] >=a[j] j<i那么a[i]为无效状态。
所以可以用优先队列进行优化 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| #include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
typedef long long LL;
#define clr(x) memset((x),0,sizeof(x))
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 100050;
int tree[maxn<<2];
LL a[maxn];
LL dp[maxn];
int q[maxn];
int l,r;
int n;
LL m;
int main()
{
scanf("%d%I64d",&n,&m);
LL cnt = 0;
int k = 0;
l = r = 0;
dp[0] = 0;
for(int i = 1;i<=n;i++)
{
scanf("%I64d",&a[i]);
if(a[i]>m) {
printf("-1\n");
return 0;
}
cnt+=a[i];
while(cnt>m) cnt-=a[++k];
while(l<r&&a[i]>=a[q[r-1]]) r--;
q[r++] = i;
while(l<r&&q[l]<k) l++;
dp[i] = 1e12;
int tt = k;
for(int j =l;j<r;j++){
// printf("%d ",q[j]);
dp[i] = min(dp[i],dp[tt]+a[q[j]]);
tt = q[j];
}
// printf("*%d ",dp[i]);
// printf("\n");
}
printf("%I64d\n",dp[n]);
return 0;
}
|