题目描述:
给定N个物品,可以连续的划分为若干个组,每个组的代价是(物品数-1+每个物品单独的代价-L)^2,求最小代价.
题目分析:
先写出DP方程:
DP[i]=min(DP[j]+(sum[i]−sum[j]+i−j−1+L)2)(j<i)
D
P
[
i
]
=
m
i
n
(
D
P
[
j
]
+
(
s
u
m
[
i
]
−
s
u
m
[
j
]
+
i
−
j
−
1
+
L
)
2
)
(
j
<
i
)
这样的转移为
n2
n
2
数据范围为 5 万 ,很明显不行.
现在化简上面的DP方程
设
f[i]=sum[i]+iC=L−1
f
[
i
]
=
s
u
m
[
i
]
+
i
C
=
L
−
1
DP[i]=min(DP[j]+(f[i]−f[j]+C)2)(j<i)
D
P
[
i
]
=
m
i
n
(
D
P
[
j
]
+
(
f
[
i
]
−
f
[
j
]
+
C
)
2
)
(
j
<
i
)
证明决策单调性:
假设 i 有两个决策点 j k (j < k)且 k 比 j 优
即
DP[j]+(f[i]−f[j]+C)2>DP[k]+(f[i]−f[k]+C)2
D
P
[
j
]
+
(
f
[
i
]
−
f
[
j
]
+
C
)
2
>
D
P
[
k
]
+
(
f
[
i
]
−
f
[
k
]
+
C
)
2
设为 1 式
题目链接:
Ac 代码:
#include <cstdio>
#include <iostream>
#define ll long long
const int maxn=50010;
int n,L;
ll a[maxn],f[maxn],s[maxn],dp[maxn],q[maxn];
double slope(ll x,ll y)
{
return (dp[x]-dp[y]+(f[x]+L)*(f[x]+L)-(f[y]+L)*(f[y]+L))/(2.0*(f[x]-f[y]));
}
int main()
{
scanf("%d%d",&n,&L);
L++;
for(int i=1;i<=n;i++) scanf("%d",&s[i]),s[i]+=s[i-1];
for(int i=1;i<=n;i++) f[i]=s[i]+i;
int l=1,r=1;dp[1]=0;
for (int i=1;i<=n;i++)
{
while(l<r&&slope(q[l],q[l+1])<=f[i]) l++;//判断优劣
dp[i]=dp[q[l]]+(f[i]-f[q[l]]-L)*(f[i]-f[q[l]]-L);//队首最优
while(l<r&&slope(q[r-1],q[r])>slope(q[r],i)) r--;//维护下凸
q[++r]=i;
}
return printf("%lld",dp[n])*0;
}