一本通1614锯木厂选址

1614:锯木厂选址

时间限制: 1000 ms         内存限制: 32768 KB

Description

从山顶上到山底下沿着一条直线种植了n棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。
木材只能按照一个方向运输:朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小。假定运输每公斤木材每米需要一分钱。

Input

输入的第一行为一个正整数n——树的个数(2≤n≤20 000)。树从山顶到山脚按照1,2……n标号。接下来n行,每行有两个正整数(用空格分开)。第i+1行含有:wi——第i棵树的重量(公斤为单位)和 di——第i棵树和第i+1棵树之间的距离,1≤wi ≤10 000,0≤di≤10 000。最后一个数dn,表示第n棵树到山脚的锯木厂的距离。保证所有树运到山脚的锯木厂所需要的费用小于2000 000 000分。

Output

输出只有一行一个数:最小的运输费用。

Sample Input

9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1

Sample Output

26

数据范围玄学:n2可过,正解应该是O(n)的,所以姑且认为是106

sol:斜率优化dp 有点像ZJOI2007仓库建设

n2暴力几乎和上一道一样,就是稍微转化下,就把统计 j ~ i 的东西全部运到 i 需要的花费变成O(1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=‘ ‘;
    while(!isdigit(ch))
    {
        f|=(ch==‘-‘); ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(‘-‘); x=-x;
    }
    if(x<10)
    {
        putchar(x+‘0‘);    return;
    }
    write(x/10);
    putchar((x%10)+‘0‘);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) write(x),putchar(‘\n‘)
const int N=200005;
const ll inf=0x7fffffffffff;
int n;
ll W[N],W_Qzh[N],Dis[N],Cost[N];
ll dp_Last[N],dp[N];
int main()
{
    int i,j;
    R(n);
    Dis[1]=0;
    for(i=1;i<=n;i++)
    {
        W[i]=read();
        W_Qzh[i]=W_Qzh[i-1]+W[i];
        Dis[i+1]=Dis[i]+read();
        Cost[i]=Cost[i-1]+W[i]*Dis[i];
    }
    for(i=1;i<=n;i++)
    {
        dp_Last[i]=Dis[i]*W_Qzh[i]-Cost[i];
    }
    memset(dp,63,sizeof dp);
    for(i=2;i<=n;i++)
    {
        for(j=1;j<i;j++)
        {
            dp[i]=min(dp[i],dp_Last[j]+Dis[i]*(W_Qzh[i]-W_Qzh[j])-(Cost[i]-Cost[j]));
        }
    }
    ll ans=inf;
    for(i=2;i<=n;i++)
    {
        ans=min(ans,dp[i]+Dis[n+1]*(W_Qzh[n]-W_Qzh[i])-(Cost[n]-Cost[i]));
    }
    Wl(ans);
    return 0;
}
/*
input
9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1
output
26
*/

n^2暴力



推导如下

j<k<i
--> dp_Last[j]+Dis[i]*(W_Qzh[i]-W_Qzh[j])-(Cost[i]-Cost[j])
--> dp_Last[j]+Dis[i]*W_Qzh[i]-Dis[i]*W_Qzh[j]-Cost[i]+Cost[j] (S2)

--> dp_Last[k]+Dis[i]*(W_Qzh[i]-W_Qzh[k])-(Cost[i]-Cost[k])
--> dp_Last[k]+Dis[i]*W_Qzh[i]-Dis[i]*W_Qzh[k]-Cost[i]+Cost[k] (S1)

若k比j优 则 S1<=S2
dp_Last[k]-Dis[i]*W_Qzh[k]+Cost[k] <= dp_Last[j]-Dis[i]*W_Qzh[j]+Cost[j]
(dp_Last[k]+Cost[k])-(dp_Last[j]+Cost[j]) <= Dis[i]*(W_Qzh[k]-W_Qzh[j])

/*

    j<k<i
    --> dp_Last[j]+Dis[i]*(W_Qzh[i]-W_Qzh[j])-(Cost[i]-Cost[j])
    --> dp_Last[j]+Dis[i]*W_Qzh[i]-Dis[i]*W_Qzh[j]-Cost[i]+Cost[j]   (S2)

    --> dp_Last[k]+Dis[i]*(W_Qzh[i]-W_Qzh[k])-(Cost[i]-Cost[k])
    --> dp_Last[k]+Dis[i]*W_Qzh[i]-Dis[i]*W_Qzh[k]-Cost[i]+Cost[k]   (S1)

    若k比j优 则 S1<=S2
    dp_Last[k]-Dis[i]*W_Qzh[k]+Cost[k] <= dp_Last[j]-Dis[i]*W_Qzh[j]+Cost[j]
    (dp_Last[k]+Cost[k])-(dp_Last[j]+Cost[j]) <= Dis[i]*(W_Qzh[k]-W_Qzh[j])

*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=‘ ‘;
    while(!isdigit(ch))
    {
        f|=(ch==‘-‘); ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar(‘-‘); x=-x;
    }
    if(x<10)
    {
        putchar(x+‘0‘);    return;
    }
    write(x/10);
    putchar((x%10)+‘0‘);
    return;
}
#define W(x) write(x),putchar(‘ ‘)
#define Wl(x) write(x),putchar(‘\n‘)
const int N=200005;
const ll inf=0x7fffffffffff;
int n;
ll W[N],W_Qzh[N],Dis[N],Cost[N];
ll dp_Last[N],dp[N],Que[N];
//(dp_Last[k]+Cost[k])-(dp_Last[j]+Cost[j]) <= Dis[i]*(W_Qzh[k]-W_Qzh[j])
inline bool Panduan(int j,int k,int i) //j<k<i
{
    ll S1=(dp_Last[k]+Cost[k])-(dp_Last[j]+Cost[j]);
    ll S2=Dis[i]*(W_Qzh[k]-W_Qzh[j]);
    return (S1<=S2)?(1):(0);
}
inline bool Panduan_Rev(int j,int k,int i) //j<k<i
{
    ll S1=((dp_Last[k]+Cost[k])-(dp_Last[j]+Cost[j]))*(W_Qzh[i]-W_Qzh[k]);
    ll S2=((dp_Last[i]+Cost[i])-(dp_Last[k]+Cost[k]))*(W_Qzh[k]-W_Qzh[j]);
    return (S1>=S2)?(1):(0);
}
int main()
{
    int i,j;
    R(n);
    Dis[1]=0;
    for(i=1;i<=n;i++)
    {
        W[i]=read();
        W_Qzh[i]=W_Qzh[i-1]+W[i];
        Dis[i+1]=Dis[i]+read();
        Cost[i]=Cost[i-1]+W[i]*Dis[i];
    }
    for(i=1;i<=n;i++)
    {
        dp_Last[i]=Dis[i]*W_Qzh[i]-Cost[i];
    }
    int Head=1,Tail=1; Que[1]=0;
    for(i=1;i<=n;i++)
    {
        while(Head<Tail&&Panduan(Que[Head],Que[Head+1],i)) Head++;
        j=Que[Head];
        dp[i]=dp_Last[j]+Dis[i]*(W_Qzh[i]-W_Qzh[j])-(Cost[i]-Cost[j]);
        while(Head<Tail&&Panduan_Rev(Que[Tail-1],Que[Tail],i)) Tail--;
        Que[++Tail]=i;
    }
    ll ans=inf;
    for(i=2;i<=n;i++)
    {
        ans=min(ans,dp[i]+Dis[n+1]*(W_Qzh[n]-W_Qzh[i])-(Cost[n]-Cost[i]));
    }
    Wl(ans);
    return 0;
}
/*
input
9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1
output
26
*/

斜率优化

原文地址:https://www.cnblogs.com/gaojunonly1/p/10421086.html

时间: 2024-10-14 21:46:25

一本通1614锯木厂选址的相关文章

【CEOI2004】锯木厂选址

[题目描述] 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂.木材只能按照一个方向运输:朝山下运.山脚下有一个锯木厂.另外两个锯木厂将新修建在山路上.你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小.假定运输每公斤木材每米需要一分钱. [输入描述] 输入的第一行为一个正整数n--树的个数(2≤n≤20 000).树从山顶到山脚按照1,2--n标号.接下来n行,每行有两个正整数(用空格分开).第i+1行含有:wi--第

动态规划(斜率优化):[CEOI2004]锯木厂选址

锯木场选址(CEOI2004) 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂. 木材只能按照一个方向运输:朝山下运.山脚下有一个锯木厂.另外两个锯木厂将新修建在山路上.你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小.假定运输每公斤木材每米需要一分钱. 任务 你的任务是写一个程序: 从标准输入读入树的个数和他们的重量与位置 计算最小运输费用 将计算结果输出到标准输出 输入 输入的第一行为一个正整数n——树的个数(2

cogs 362. [CEOI2004]锯木厂选址

★★★   输入文件:two.in   输出文件:two.out   简单对比 时间限制:0.1 s   内存限制:32 MB 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂.木材只能按照一个方向运输:朝山下运.山脚下有一个锯木厂.另外两个锯木厂将新修建在山路上.你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小.假定运输每公斤木材每米需要一分钱. 输入 输入的第一行为一个正整数n——树的个数(2≤n≤20 000).

【Bsoj2684】锯木厂选址(斜率优化)

Description 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂.木材只能按照一个方向运输:朝山下运.山脚下有一个锯木厂.另外两个锯木厂将新修建在山路上.你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小.假定运输每公斤木材每米需要一分钱.任务你的任务是写一个程序:从标准输入读入树的个数和他们的重量与位置计算最小运输费用将计算结果输出到标准输出(2≤n≤20 000) Solution 设\(S[i]\)为重量前

锯木厂选址

这是我斜率DP第一个没有一遍AC的,原因是第一遍忘开long long了. 这一题比较特殊,细心的同学一定发现了,递推式不带f. 为了方便,设d数组的后缀和为sd[i]=sd[i+1]+d[i],设k数组的前缀和为sk[i]=sk[i-1]+k[i](k[i]即是题目中的w[i]) 设f[i]为第二个锯木厂选在i时的最小值,假设第一个锯木厂在j,从1~j-1运到j的和是k[p]*(sd[p]-sd[j]),p∈[1,n],从j+1~i-1运到i的和是k[p]*(sd[p]-sd[i]),p∈[j

[CEOI2004]锯木厂选址

link 做这种题就应该去先写个暴力代码 #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1

DP总结 ——QPH

常见优化 单调队列 形式 dp[i]=min{f(k)} dp[i]=max{f(k)} 要求 f(k)是关于k的函数 k的范围和i有关 转移方法 维护一个单调递增(减)的队列,可以在两头弹出元素,一头压入元素. 队列中维护的是两个值.一个是位置,这和k的范围有关系,另外一个是f(k)的值,这个用来维护单调性,当然如果f(k)的值可以利用dp值在O(1)的时间内计算出来的话队列中可以只维护一个表示位置的变量. 枚举到一个i的时候,首先判断队首元素的位置是否已经不满足k的范围了,如果不满足就将队首

DP常用优化

DP常用优化 一.前缀和优化 当遇到类似:\(f[i] = \sum_{j = k}^{i} g[j]\)的转移时,可以通过预处理出\(g[i]\)的前缀和\(s[i]\),将\(O(n)\)的求和转换为\(O(1)?\)的操作. [HAOI2009]逆序对数列 [HAOI2008]木棍分割 二分答案+dp P4099 [HEOI2013]SAO 树形dp 二.决策单调性--单调队列优化 接下来几种优化方法主要是对1d/1d dp的优化,其中xd/yd dp指的是状态数有\(n^x\)种,每个状

hdu 4396 More lumber is required(最短路)

hdu 4396 More lumber is required Description "More lumber(木材) is required" When the famous warcrafts player Sky wants to build a Central Town, he finds there is not enough lumber to build his Central Town. So he needs to collect enough lumber. H