[CEOI2004] 锯木厂选址

版权声明:全文无版权,目前博客已搬迁至https://bill.moe https://blog.csdn.net/Bill_Yang_2016/article/details/54671191

题目描述

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


输入格式

输入的第一行为一个正整数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分。


输出格式

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


样例数据

样例输入

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

样例输出

26


题目分析

用sumw[i]记录ij=1w[j]维护前缀和
用sumd[i]记录i1j=1d[j]维护前缀和
Cost[i]表示将第一个锯木厂建在i的位置时,1~i第一段的木材运到i的费用:Cost[i]=Cost[i-1]+sumw[i-1]*d[i-1]
接着就可以用列出状态转移方程,设f[i]表示第二个锯木厂建在i时的费用
f[i]=min{Cost[j]-sumw[j](sumd[i]-sumd[j])-sumw[i](sumd[n+1]-sumd[i])}

y=sumw[j]*sumd[j]
x=sumw[j]
k=sumd[i]
维护下凸包,斜率优化乱搞即可。


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
    int num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
int n,sumw[50005],sumd[50005],w[50005],d[50005],Cost[50005],Q[50005],f[50005],ans=0x7fffffff/2;
double Slope(int j,int k) { //计算斜率
    return (double)(sumw[k]*sumd[k]-sumw[j]*sumd[j])/(sumw[k]-sumw[j]);
}
int main() {
    n=Get_Int();
    for(int i=1; i<=n; i++) {
        w[i]=Get_Int();
        d[i]=Get_Int();
    }
    for(int i=1; i<=n+1; i++) {
        sumw[i]=sumw[i-1]+w[i];
        sumd[i]=sumd[i-1]+d[i-1];
        Cost[i]=Cost[i-1]+sumw[i-1]*d[i-1]; //Cost[i]表示把第一个锯木厂设在i,第一段木材运到i的总费用
    }
    int Left=1,Right=1;
    Q[1]=1;
    f[1]=0;
    for(int i=2; i<=n; i++) {
        while(Left<Right&&Slope(Q[Left],Q[Left+1])<=sumd[i])Left++; //维护队首(删除非最优决策)
        int Front=Q[Left];
        f[i]=sumw[Front]*sumd[Front]-sumd[i]*sumw[Front]+Cost[n+1]-sumw[i]*(sumd[n+1]-sumd[i]); //计算当前f
        while(Left<Right&&Slope(Q[Right-1],Q[Right])>=Slope(Q[Right],i))Right--; //维护队尾(维护下凸包性质)
        Q[++Right]=i; //入队
        ans=min(ans,f[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

展开阅读全文

没有更多推荐了,返回首页