1. 程式人生 > >【Bsoj2684】鋸木廠選址(斜率優化)

【Bsoj2684】鋸木廠選址(斜率優化)

任務 post def sin solution name line () des

Description

從山頂上到山底下沿著一條直線種植了n棵老樹。當地的政府決定把他們砍下來。為了不浪費任何一棵木材,樹被砍倒後要運送到鋸木廠。木材只能按照一個方向運輸:朝山下運。山腳下有一個鋸木廠。另外兩個鋸木廠將新修建在山路上。你必須決定在哪裏修建兩個鋸木廠,使得傳輸的費用總和最小。假定運輸每公斤木材每米需要一分錢。任務你的任務是寫一個程序:從標準輸入讀入樹的個數和他們的重量與位置計算最小運輸費用將計算結果輸出到標準輸出(2≤n≤20 000)

Solution

\(S[i]\)為重量前綴和,\(Sd[i]\)為距離前綴和,\(d[i]\)為第\(i\)棵樹到第\(i+1\)棵樹的距離

那麽第一個鋸木廠費用:\(Cost[i]=Cost[i-1]+S[i-1]*d[i-1]\)

而到第二個鋸木廠費用:\(W(i,j)=Cost[j]-Cost[i-1]-S[i-1]*(Sd[j]-Sd[i-1])\)

那麽\(Ans=min\{Cost[j]+W(j+1,i)+W(i+1,n+1)\}\)

亂搞一下發現斜率式,此時\(i>k>j\),且\(k\)\(j\)

\(\frac{S[j]*Sd[j]-S[k]*Sd[k]}{S[j]-S[k]}<Sd[i]\)

然後就完了

Tips:

? 由於\(S[k]>S[j]\)所以\(S[j]-S[k]<0\) 把斜率式變形的時候記得變符號

? 因為不知道在哪設鋸木廠最優,用一個\(Ans\)變量隨時更新,否則WA

? 雖然答案在int範圍,但是如果化除為乘的話中間結果要long long

Code

#include <cstdio>
#include <algorithm>
#define N 20010
using namespace std;

int n,d[N],s[N],sd[N],cost[N],Ans;
int l,r,q[N];

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1
;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void Init(){ n=read(); for(int i=1;i<=n;++i){ s[i]=s[i-1]+read(),d[i]=read(); sd[i]=sd[i-1]+d[i-1]; cost[i]=cost[i-1]+d[i-1]*s[i-1]; } s[n+1]=s[n]; sd[n+1]=sd[n]+d[n]; cost[n+1]=cost[n]+d[n]*s[n]; } inline int f(int j,int k){return s[j]*sd[j]-s[k]*sd[k];} inline int g(int j,int k){return s[j]-s[k];} int h(int i,int j){return cost[n+1]-s[j]*(sd[i]-sd[j])-s[i]*(sd[n+1]-sd[i]);} inline void DP(){ l=r=1;Ans=1e9; for(int i=1;i<=n;++i){ while(l<r&&f(q[l],q[l+1])>sd[i]*1ll*g(q[l],q[l+1])) l++; int j=q[l]; Ans=min(Ans,h(i,j)); while(l<r&&f(q[r],i)*1ll*g(q[r-1],q[r])<g(q[r],i)*1ll*f(q[r-1],q[r])) r--; q[++r]=i; } } int main(){ Init(); DP(); printf("%d\n",Ans); return 0; }

【Bsoj2684】鋸木廠選址(斜率優化)