[题解]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)。
下面给出代码:
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 }
至此,此题完美解决。