bzoj 1096 仓库建设 —— 斜率优化DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1096

设 f[i] 为 i 作为最后一个仓库时前 i 个工厂的答案,最后的答案当然是 f[n];

f[i] = min{ f[j] + ∑(j+1<=k<=i)p[k]*(x[i]-x[k]) + c[i] } , 1<=j<i

令 s[i] = ∑(1<=j<=i)p[j],t[i] = ∑(1<=j<=i)p[j]*x[j]

则 f[i] = min{ f[j] + x[i]*(s[i]-s[j]) - (t[i]-t[j]) + c[i] }

移项,得到 f[j] + t[j] = x[i]*s[j] - x[i]*s[i] + t[i] - c[i] + f[i]

x[i] 单增,s[j] 单增,可以看出是要维护一个下凸包。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef double db;
typedef long long ll;
int const xn=1e6+5;
int n,x[xn],p[xn],c[xn],q[xn];
ll s[xn],t[xn],f[xn];
int rd()
{
  int ret=0,f=1; char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
  while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return f?ret:-ret;
}
db y(int i){return f[i]+t[i];}
db slp(int a,int b){return (y(b)-y(a))/(s[b]-s[a]);}
int main()
{
  n=rd();
  for(int i=1;i<=n;i++)
    {
      x[i]=rd(),p[i]=rd(),c[i]=rd();
      s[i]=s[i-1]+p[i]; t[i]=t[i-1]+(ll)p[i]*x[i];
    }
  int hd=0,tl=0;
  for(int i=1;i<=n;i++)
    {
      while(hd<tl&&slp(q[hd],q[hd+1])<x[i])hd++;
      f[i]=f[q[hd]]+(ll)x[i]*(s[i]-s[q[hd]])-t[i]+t[q[hd]]+c[i];
      while(hd<tl&&slp(q[tl-1],q[tl])>slp(q[tl-1],i))tl--;
      q[++tl]=i;
    }
  printf("%lld\n",f[n]);
  return 0;
}

 

posted @ 2018-11-07 11:00  Zinn  阅读(62)  评论(0编辑  收藏