题目描述:
给你一个序列,让你分割K次,每次分割可以获得分割点前后两个块和乘积的价值
求最大价值
题目分析:
首先分割次序对总价值没有影响。
f[k][i]表示前第k次分割点为i的所获得的最大分割价值.
f[i][k]=f[j][k−1]+(sum[i]−sum[j])∗sum[j]
f
[
i
]
[
k
]
=
f
[
j
]
[
k
−
1
]
+
(
s
u
m
[
i
]
−
s
u
m
[
j
]
)
∗
s
u
m
[
j
]
朴素转移
O(N2∗K)
O
(
N
2
∗
K
)
考虑斜率优化.
设j1 > j2 且 j1 优
套用斜率优化为 O(n∗k) O ( n ∗ k )
不要忘了滚动数组.
题目链接:
Ac 代码:
#include <cstdio>
#include <iostream>
#define ll long long
const int maxm=110000;
ll dp[maxm][2],f,sum[maxm],x[maxm];
int to[210][maxm];
int dl[maxm];
int n,k;
double slop(int j,int k,int w)
{
return (double)(sum[k]*sum[k]-sum[j]*sum[j]+dp[j][w]-dp[k][w])/(double)(sum[k]-sum[j]);
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i]);
sum[i]=sum[i-1]+x[i];
if(sum[i]==sum[i-1]) i--,n--;
}
k=k<n?k:n-1;
for(int i=1;i<=k;i++)
{
int v=i&1;
int t=v^1;
int l=1,r=0;
for(int j=i;j<=n;j++)
{
while(l<r&&slop(dl[r-1],dl[r],t)>slop(dl[r],j-1,t)) r--;
dl[++r]=j-1;
while(l<r&&slop(dl[l],dl[l+1],t)<sum[j]) l++;
dp[j][v]=dp[dl[l]][t]+(sum[j]-sum[dl[l]])*sum[dl[l]];
to[i][j]=dl[l];
}
}
ll ans=0;
for(int i=1;i<=n;i++) ans=std::max(ans,dp[i][k&1]);
printf("%lld\n",ans);
return 0;
}