【BZOJ 1096】[ZJOI2007]仓库建设

【链接】 链接
【题意】

在这里输入题意

【题解】

设f[i]表示在第i个地方设立一个仓库,且前面符合要求的最小花费。 则 $f[i] = min(f[j] + c[i] + dis[i]*(sump[i] - sump[j])-(sumdp[i]-sumdp[j]));$ 其中 sump[]是p[]的前缀和,即$sump[i] = p[1]+p[2]+..+p[i]$ sumdp[]是x[]*p[]的前缀和,即$sumdp[i] = x[1]*p[1]+x[2]*p[2]+...+x[i]*p[i]$ 这个表示j+1..i这一段的所有物品都运输到i这个仓库。 自己用笔写一下就知道了。 显然是个N^2算法。 斜率优化。 设x< y,且y优于x. balabala就能得到 $frac{f[y]+sumdp[y]-(f[x]+sumdp[x])}{sump[y]-sump[x]}< dis[i]$ 又dis是单调递增的。 这是一个经典的斜率优化问题了。

【错的次数】

在这里输入错的次数

【反思】

在这里输入反思

【代码】

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 1e6;

int n,dl[N+10],h,t;
ll dis[N+10],p[N+10],c[N+10],sump[N+10],sumdp[N+10];
ll dp[N+10];

double ju(int x,int y)
{
    double fenzi = dp[y] + sumdp[y] - (dp[x]+sumdp[x]);
    double fenmu = sump[y] - sump[x];
    return fenzi/fenmu;
}

int main()
{
    //freopen("F:\rush.txt","r",stdin);
    scanf("%d",&n);
    for (int i = 1;i <= n;i++)
    {
        scanf("%lld%lld%lld",&dis[i],&p[i],&c[i]);
        sump[i] = sump[i-1] + p[i];
        sumdp[i] = sumdp[i-1] + dis[i]*p[i];
    }

    for (int i = 1;i <= N;i++) dp[i] = 1e18;
    h = 1,t = 1;
    dl[1] = 0;
    for (int i = 1;i <= n;i++)
    {
        while (h < t && ju(dl[h],dl[h+1])<dis[i]) h++;
        int j = dl[h];
        dp[i] = min(dp[i],dp[j] + c[i] +
                            dis[i]*(sump[i]-sump[j])-(sumdp[i]-sumdp[j]));
        while (h < t && ju(dl[t-1],dl[t]) > ju(dl[t],i)) t--;
        dl[++t] = i;
    }
    printf("%lld
",dp[n]);
    return 0;
}

原文地址:https://www.cnblogs.com/AWCXV/p/7637996.html