ceoi2004锯木厂选址

http://www.elijahqi.win/archives/459
Description
从山顶上到山底下沿着一条直线种植了n棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。
木材只能按照一个方向运输:朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小。假定运输每公斤木材每米需要一分钱。
你的任务是编写一个程序,从输入文件中读入树的个数和他们的重量与位置,计算最小运输费用。
Input
输入的第一行为一个正整数n——树的个数(2≤n≤20000)。树从山顶到山脚按照1,2……n标号。接下来n行,每行有两个正整数(用空格分开)。第i+1行含有:wi——第i棵树的重量(公斤为单位)和 di——第i棵树和第i+1棵树之间的距离,1≤wi≤10000,0≤di≤10000。最后一个数dn,表示第n棵树到山脚的锯木厂的距离。保证所有树运到山脚的锯木厂所需要的费用小于2000000000分。
Output
输出只有一行一个数:最小的运输费用。
Sample Input
9
12
21
33
11
32
16
21
12
11
Sample Output
26


#include <cstdio>
#define N 22000
#define MAX 0x7fffffff
inline int read(){
    int x=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int cost[N],sd[N],sw[N],d[N],w[N],n,min,q[N],f[N];
inline double slope(int k1,int k2){
    double kk=1;
    return (kk*sw[k1]*sd[k1]-kk*sw[k2]*sd[k2])/(sw[k1]-sw[k2]);
}
inline int all(int k1,int k2){  //运送k1~k2之间的所有树 
    return (cost[k2]-cost[k1-1]-sw[k1-1]*(sd[k2]-sd[k1-1]));
}
int main(){
    freopen("two.in","r",stdin);
    //freopen("two.out","w",stdout);
    n=read();
    for (int i=1;i<=n;++i) w[i]=read(),d[i]=read();
    for (int i=1;i<=n+1;++i) {//
        sw[i]=sw[i-1]+w[i];
        sd[i]=sd[i-1]+d[i-1];
        cost[i]=cost[i-1]+sw[i-1]*d[i-1];
    }
    q[0]=N;q[1]=1;
    int k1=1,k2=1;
    min=f[1]=MAX;
    for (int i=2;i<=n;++i){
        while ((k1<k2)&&(slope(q[k1],q[k1+1])<=sd[i])) ++k1;
        f[i]=cost[q[k1]]+all(q[k1]+1,i)+all(i+1,n+1);
        if (min>f[i]) min=f[i];
        int tmp=slope(q[k2],i);
        while (k1<k2&&(slope(q[k2-1],q[k2])>=tmp)) tmp=slope(q[--k2],i);
        q[++k2]=i;
    }
    printf("%d",min);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值