ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【ZJOI 2007】仓库建设

2020-03-24 11:00:33  阅读:245  来源: 互联网

标签:pre ZJOI min 仓库 long int 2007 ans dp


\(Solution\) 1

这道题不算很难,这里我提供两种做法。

设 dp[i] 为在 i 位上设置仓库,前面工厂都解决了的最小花费。首先我们可以列出 DP 式:

\[dp[i]=min(dp[j]+sigma(p[k]*(x[i]-x[k])))+c[i] \]

\[dp[i]=min(dp[j]+x[i]*sigma(p[k])-sigma(p[k]*x[k]))+c[i] \]

我们定义 pre 数组为 p 数组的前缀和,s 数组为 \(p[i]*x[i]\) 的前缀和。用这两个数组乱搞就可以用斜率优化了。

\(Code\) 1

贴一下代码。

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;

const int N = 1e6 + 5;

int n, q[N], head, tail;
ll c[N], x[N], pre[N], dp[N], s[N];

int read() {
    int x = 0, f = 1; char s;
    while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
    while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
    return x * f;
}

double slope(const int j1, const int j2) {
    return 1.0 * (dp[j2] + s[j2] - dp[j1] - s[j1]) / (pre[j2] - pre[j1]);
}

int main() {
    n = read();
    for(int i = 1; i <= n; ++ i) {
        x[i] = read(), pre[i] = read(), c[i] = read();
        s[i] = s[i - 1] + x[i] * pre[i]; pre[i] += pre[i - 1];
    }
    for(int i = 1; i <= n; ++ i) {
        while(head < tail && slope(q[head], q[head + 1]) < x[i]) ++ head;
        int j = q[head];
        dp[i] = dp[j] + x[i] * (pre[i] - pre[j]) - (s[i] - s[j]) + c[i];
        while(head < tail && slope(q[tail], i) < slope(q[tail - 1], q[tail])) -- tail;
        q[++ tail] = i;
    }
    ll ans = dp[n];
    printf("%lld\n", ans);
    return 0;
}

这里说一下一个疑惑。我们 dp[n] 的定义是保证 n 一定会是仓库的最小值。但是,题目并不保证工厂 n 的材料不为 0。也就是说,我们有可能选取之前的 dp 值,保证那个坐标的后面工厂都没有材料(也就不需要设置仓库了)。

我是这样写的:

    ll ans = dp[n];
    for(int i = n; i >= 1; -- i) {
        if(pre[i] - pre[i - 1]) break;
        ans = min(ans, (long long) dp[i - 1]);
    }
    printf("%lld\n", ans);

HYSBZ 交上去是错的?好懵???(我怀疑是数据有锅或者是我想错了

\(Solution\) 2

这是我自己的方法, Luogu 可以过,只是 HYSBZ 过不去 QwQ。(应该是爆 long long 了,如果不是请大佬在评论区指教)

dp 数组,pre 数组定义同上。这里的 s 数组有点不一样,表示前面所有工厂的材料都转移到 i 工厂的花费。则可以转化为:

\[dp[i]=min(dp[j]+s[i]-pre[j]*(x[i]-x[j])-s[j])+c[i] \]

其中 \(s[i]-pre[j]*(x[i]-x[j])-s[j]\) 是将之前的 sigma 感性理解:i 与区间 \([j+1,i]\) 的数匹配,每个地方向 i 的花费。我们首先 \(s[i]-s[j]\),剩下的就是 [1,j] 区间到 i 的花费要减去,而 \(pre[j]*(x[i]-x[j])\) 正是区间的成品数量乘上路程。

我们设 \(f[i]=s[i]\) ,\(g[i]=pre[i]*x[i]-s[i]\),然后乱搞出斜率优化?

\(Code\) 2

#include <cstdio>
#include <iostream>
using namespace std;

const int N = 1e6 + 5;

int n, q[N], head, tail;
double c[N], x[N], pre[N], g[N], dp[N], s[N];

int read() {
    int x = 0, f = 1; char s;
    while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
    while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
    return x * f;
}

double slope(const int j1, const int j2) {
    return (dp[j2] + g[j2] - dp[j1] - g[j1]) / (pre[j2] - pre[j1]);
}

int main() {
    n = read();
    for(int i = 1; i <= n; ++ i) {
        x[i] = read(), pre[i] = read(), c[i] = read();
        pre[i] += pre[i - 1]; s[i] = s[i - 1] + 1.0 * (x[i] - x[i - 1]) * pre[i - 1];
        g[i] = pre[i] * x[i] - s[i];
    }
    for(int i = 1; i <= n; ++ i) {
        while(head < tail && slope(q[head], q[head + 1]) < x[i]) ++ head;
        dp[i] = dp[q[head]] + s[i] - pre[q[head]] * (x[i] - x[q[head]]) - s[q[head]] + c[i];
        while(head < tail && slope(q[tail], i) < slope(q[tail - 1], q[tail])) -- tail;
        q[++ tail] = i;
    }
    long long ans = dp[n];
    for(int i = n; i >= 1; -- i) {
        if(pre[i] - pre[i - 1]) break;
        ans = min(ans, (long long) dp[i - 1]);
    }
    printf("%lld\n", ans);
    return 0;
}

如果有错误,请大佬在评论区留言哟!

标签:pre,ZJOI,min,仓库,long,int,2007,ans,dp
来源: https://www.cnblogs.com/AWhiteWall/p/12557573.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有