您的位置:首页 > 其它

【HNOI2008】【斜率优化DP】玩具装箱

2013-04-03 10:01 253 查看
很直观的一维DP

状态:f[i]表示将前i个玩具打包的最小费用。

朴素方程:f[i] = min{f[j] + (w[j+1][i]
- L) ^ 2}
(w[j+1][i] =∑
Ck + i - (j + 1),j < i,k
∈[j+1,i])

但是直接朴素会TLE,所以我们考虑将方程变形,挖掘方程的性质。

令sum[i]=
∑Ck k∈
[1,i]

->
f[i] = min{
f[j] + (sum[i] - sum[j] + i - j - 1
- L) ^ 2 }

令s[i]
= sum[i] + i

->
f[i] = min{ f[j] + (s[i] - s[j] -
L - 1) ^ 2}

令m
= s[i] - l - 1

->
f[i] = min{ f[j] + (m - s[j]) ^ 2 }

->
f[i] = min{ f[j] + m ^ 2
- 2 * m * s[j] + s[j] ^ 2 }

考虑∀决策k,k
< i且决策k比决策j更优

->f[k]
- 2 * m * s[k] + s[k] ^ 2 + m ^ 2 < f[j] - 2 * m * s[j] + s[j] ^ 2 + m ^ 2

->
f[k] - 2 * m * s[k] + s[k] ^ 2 < f[j] - 2 * m
* s[j] + s[j] ^ 2

->
(f[k] + s[k] ^ 2) - (f[j] + s[j] ^ 2)/
(s[k] - s[j]) < 2 * m

这样我们可以令x
= s[i],y = f[j] + s[j] ^ 2

则可以将每个决策看作两个点,只要满足上述条件则决策更优,使用一个队列来维护最优决策就可以了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 50000 + 10;
int deq[maxn];
long long c[maxn],S[maxn],f[maxn];
int n,l;

void init()
{
freopen("bzoj1010.in","r",stdin);
freopen("bzoj1010.out","w",stdout);
}

inline double slope(int i,int j)
{
return (double)((f[i] + S[i] * S[i] - f[j] - S[j] * S[j]) / (S[i] - S[j]));
}

void readdata()
{
memset(c,0,sizeof(c));
memset(S,0,sizeof(S));
scanf("%d%d",&n,&l);
for(int i = 1;i <= n;i++)
{
scanf("%lld",&c[i]);
c[i] += c[i-1];S[i] = c[i] + i;
}
}

void solve()
{
memset(f,0,sizeof(f));
int s = 0,e = 0;
for(int i = 1;i <= n;i++)
{
long long m = S[i] - l - 1;
while(s < e && slope(deq[s+1],deq[s]) <= 2 * m)++s;
int j = deq[s];
f[i] = f[j] + (m - S[j]) * (m - S[j]);
while(s < e && slope(deq[e],deq[e-1]) >= slope(i,deq[e]))--e;
deq[++e] = i;
}
printf("%lld\n",f
);
}

int main()
{
init();
readdata();
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: