BZOJ3437 小P的牧場(斜率優化dp)
題目link:http://www.lydsy.com/JudgeOnline/problem.php?id=3437;
略略讀一下題,發現這題是一道dp
有一些牧場:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
其中編號大的可以管住編號小的.
a[i]表示建站費用 b[i]表示養殖奶牛數目
dp方程大概就出來了
f[i]=min(f[j]+cost(j+1,i)) (0<j<i);
可是有些問題需要O(1)計算cost(j+1,i);
怎麽辦呢?
於是我想了一個很不清真的計算方法
開了兩個數組
形式化的講:
sumb[i]=sumb[i-1]+b[i]
sum[i]=sum[i-1]+sumb[i-1]
有什麽意義呢
sumb[i]表示b的前綴和
sum[i]表示如果1到i-1都要由i控制的運輸費用;(不計建站費用)
第一個轉移是顯然的
第二個轉移是因為可以將運輸想象成兩部分,先運到i-1,再運到i
然後就可以O(1)計算cost(j+1,i)了
cost(j+1,i)=sum[i]-sum[j]-sumb[j]*(i-j)
這個文字比較難解釋,還是看圖吧
矩形的長指i-k
矩形的寬指b[k]
(j<k<i)
則矩形的面積指該點對sum的貢獻
很顯然,現在cost=3號區域的面積
已知:總面積=sum[i]; 1號區域面積=sum[j]; 2號區域面積=sumb[j]*(i-j);
所以cost(j+1,i)=sum[i]-sum[j]-sumb[j]*(i-j);
/-----------------分割線-----------------/
現在f[i]=min(f[j]+sum[i]-sum[j]-sumb[j]*(i-j)+a[i])
f[i]=min(f[j]-sum[j]-sumb[j]*(i-j))+sum[i]+a[i];
開始斜率優化!
1.去掉min,確認是維護下凸包
2.f[i]=f[j]-sum[j]-sumb[j]*(i-j)+sum[i]+a[i];
3.化式子f[i]+i*sumb[j]=f[j]-sum[j]+sumb[j]*j+const(這一坨是跟j無關的常量)
4.對應 b +k x =y
然後一切就簡單了
但是這題有坑點,a,b數組要開long long,題中的數據範圍是假的
我因為括號寫錯了位置,WA一上午,最後只改了一個字符就AC了!QAQ
AC代碼:
#include<bits/stdc++.h> using namespace std; const int N=1000010; int n,h,t,q[N]; long long a[N],b[N],sum[N],sumb[N],f[N]; inline double X(const int &j){ return sumb[j]; } inline double Y(const int &j){ return f[j]-sum[j]+sumb[j]*j; } inline double Rate(const int i,const int j){ return (Y(i)-Y(j))/(X(i)-X(j)); } int main(){ scanf("%d",&n); for (int i=1; i<=n; i++) scanf("%lld",&a[i]); for (int i=1; i<=n; i++) scanf("%lld",&b[i]); for (int i=1; i<=n; i++){ sumb[i]=sumb[i-1]+b[i]; sum[i]=sum[i-1]+sumb[i-1]; } for (int i=1; i<=n; i++){ while (h<t&&Rate(q[h],q[h+1])<i) ++h; f[i]=f[q[h]]+sum[i]-sum[q[h]]-sumb[q[h]]*(i-q[h])+a[i]; while(h<t&&Rate(q[t-1],q[t])>Rate(q[t],q[i])) t--; q[++t]=i; } printf("%lld",f[n]); }
不知道讀者明白這題了沒有,不明白的話請聯系Yuhuger
BZOJ3437 小P的牧場(斜率優化dp)