[题解]CEOI 2004 锯木厂选址

【题目描述】

从山顶上到山底下沿着一条直线种植了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分。
【输出】
输出只有一行一个数:最小的运输费用。
【样例输入】
9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1
【样例输出】
26

-----------------------------------------------------------我是分界线----------------------------------------------------------------------

【题解】

裸的单调队列优化DP。今天zyy脑子坏掉了,一直脑抽啊脑抽,一直调不过调不过,然后发现是方程推错了= =|| 。各种纠结之后,仔细一看是符号弄错了!!! 看来功力还是不够啊。。

选择两个锯木厂,那么整条河流就被分成了三段,花费计算如下:

f[i]=w[1]*(x[j]-x[1])+w[2]*(x[j]-x[2])+...+w[j]*(x[j]-x[j])                                             //1~j

      +w[j+1]*(x[i]-x[j+1])+w[j+2]*(x[i]-x[j+2])+...+w[i]*(x[i]-x[i])                                //j+1~i

      +w[i+1]*(x[n+1]-x[i+1])+w[i+2]*(x[n+1]-x[i+2])+...+w[n]*(x[n+1]-x[n])             //i+1~n

化简之后:f[i]=Delta+Sum[w[j]]*x[j]-Sum[w[j]]*x[i]  

我们令X=Sum[w[j]] ,Y=Sum[w[j]]*x[j] 。 当i固定时,维护(X,Y)。

下面给出代码:

View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 #define MAXN 20020
 5 #define INF 0x7fffffff
 6 int w[MAXN],d[MAXN],x[MAXN];
 7 int q[MAXN],Front,Back;
 8 int n;
 9 int ans,Sum,Delta;
10 inline void Get_int(int &Ret)
11 {
12     char ch;
13     bool flag=false;
14     for(;ch=getchar(),ch<'0'||ch>'9';)
15         if(ch=='-')
16             flag=true;
17     for(Ret=ch-'0';ch=getchar(),ch>='0'&&ch<='9';Ret=Ret*10+ch-'0');
18     flag&&(Ret=-Ret);
19 }
20 void Read()
21 {
22     Get_int(n);
23     int i;
24     for(i=1;i<=n;i++)
25     {
26         Get_int(w[i]),Get_int(d[i]);
27         x[i+1]=x[i]+d[i];
28         Sum+=x[i]*w[i];
29         w[i]+=w[i-1];
30     }
31 }
32 inline double Get(int i,int j)
33 {
34     return (double)(w[i]*x[i]-w[j]*x[j])/(double)(w[i]-w[j]);
35 }
36 void Work()
37 {
38     ans=INF;
39     Front=0,Back=-1;
40     int i;
41     for(i=1;i<=n;i++)
42     {
43         while(Front<Back&&Get(q[Back],q[Back-1])>=Get(i,q[Back]))
44             Back--;
45         q[++Back]=i;
46         while(Front<Back&&Get(q[Front],q[Front+1])<=x[i])
47             Front++;
48         Delta=w[i]*x[i]+x[n+1]*(w[n]-w[i])-Sum;
49         ans=min(ans,Delta+w[q[Front]]*x[q[Front]]-x[i]*w[q[Front]]);
50     }
51     printf("%d\n",ans);
52 }
53 int main()
54 {
55     Read();
56     Work();
57     return 0;
58 }

 

至此,此题完美解决。

转载于:https://www.cnblogs.com/CQBZOIer-zyy/archive/2013/03/18/2966739.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值