【题目描述】
从山顶上到山底下沿着一条直线种植了n棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。
木材只能按照一个方向运输:朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建两个锯木厂,使得传输的费用总和最小。假定运输每公斤木材每米需要一分钱。
【输入描述】
输入的第一行为一个正整数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分。
【输出描述】
输出只有一行一个数:最小的运输费用。
【分析】
斜率优化,看别人的,只提供代码,以后慢慢总结。(觉得自己实在是太水了==)
贴上链接:
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=;
const int INF=;
using namespace std;
int sw[maxn],sd[maxn],cost[maxn],Q[*maxn];
inline double g(int i,int j) {return ((sw[i]*sd[i]-sw[j]*sd[j]*1.0)/(sw[i]-sw[j]));}
inline int all(int i,int j) {return cost[j]-cost[i-]-sw[i-]*(sd[j]-sd[i-]);}
int main()
{
int n,i,front=,rear=,ans=INF,w,d;
//文件操作
freopen("two.in","r",stdin);
freopen("two.out","w",stdout);
memset(sw,,sizeof(sw));
memset(sd,,sizeof(sd));
memset(cost,,sizeof(cost));
scanf("%d",&n);
//从山顶往下输入
for (i=;i<=n;i++)
{
scanf("%d%d",&w,&d);
sw[i]=sw[i-]+w;
sd[i+]=sd[i]+d;
cost[i+]=cost[i]+sw[i]*d;
}Q[]=;//初始化队列
for (i=;i<=n;i++)
{
//满足队列单调性
while (front<rear && g(Q[front],Q[front+])<=sd[i]) front++;
ans=min(ans,cost[Q[front]]+all(Q[front]+,i)+all(i+,n+));
while (front<rear && g(Q[rear],i)<g(Q[rear-],Q[rear]))rear--;//弹出队列
Q[++rear]=i;//加入队列
}
printf("%d\n",ans);
return ;
}