BZOJ1492 貨幣兌換 CDQ分治優化DP

1492: [NOI2007]貨幣兌換Cash

Time Limit: 5 Sec  Memory Limit: 64 MB

Description

小Y最近在一家金券交易所工做。該金券交易所只發行交易兩種金券:A記念券(如下簡稱A券)和 B記念券(如下
簡稱B券)。每一個持有金券的顧客都有一個本身的賬戶。金券的數目能夠是一個實數。天天隨着市場的起伏波動,
兩種金券都有本身當時的價值,即每一單位金券當天能夠兌換的人民幣數目。咱們記錄第 K 天中 A券 和 B券 的
價值分別爲 AK 和 BK(元/單位金券)。爲了方便顧客,金券交易所提供了一種很是方便的交易方式:比例交易法
。比例交易法分爲兩個方面:(a)賣出金券:顧客提供一個 [0,100] 內的實數 OP 做爲賣出比例,其意義爲:將
 OP% 的 A券和 OP% 的 B券 以當時的價值兌換爲人民幣;(b)買入金券:顧客支付 IP 元人民幣,交易所將會兌
換給用戶總價值爲 IP 的金券,而且,知足提供給顧客的A券和B券的比例在第 K 天剛好爲 RateK;例如,假定接
下來 3 天內的 Ak、Bk、RateK 的變化分別爲:
假定在第一天時,用戶手中有 100元 人民幣可是沒有任何金券。用戶能夠執行如下的操做:
注意到,同一天內能夠進行屢次操做。小Y是一個頗有經濟頭腦的員工,經過較長時間的運做和行情測算,他已經
知道了將來N天內的A券和B券的價值以及Rate。他還但願可以計算出來,若是開始時擁有S元錢,那麼N天后最多能
夠得到多少元錢。

 

Input

輸入第一行兩個正整數N、S,分別表示小Y能預知的天數以及初始時擁有的錢數。接下來N行,第K行三個實數AK、B
K、RateK,意義如題目中所述。
測試數據設計使得精度偏差不會超過10^-7。
對於40%的測試數據,知足N ≤10;
對於60%的測試數據,知足N ≤1 000;
對於100%的測試數據,知足N ≤100 000;
對於100%的測試數據,知足:0<AK≤10;0<BK≤10;0<RateK≤100;MaxProfit≤10^9。
【提示】
1.輸入文件可能很大,請採用快速的讀入方式。
2.必然存在一種最優的買賣方案知足:
每次買進操做使用完全部的人民幣;
每次賣出操做賣出全部的金券。
 

Output

只有一個實數MaxProfit,表示第N天的操做結束時可以得到的最大的金錢數目。答案保留3位小數。html

Sample Input

3 100
1 1 1
1 2 2
2 2 3

Sample Output

225.000

HINT

 

(轉載請註明原文地址:http://www.cnblogs.com/LadyLex/p/8028556.html )ide

終於把CDQ維護凸殼學了……測試

那麼既然題目已經給提示2了,咱們就能夠針對性的設$f[i]$爲第i天得到的最多A券數,$ans[i]$爲第i天的最大獲利,那麼咱們要求的就是ans[n]了。優化

這個dp顯然是能夠$O(n^{2})$解決的……可是這樣拿不了後面的分數。spa

而後你就想啊,確定要優化啊……而後你就能夠化出一個斜率的式子來。設計

對於$ans[i]$的決策,咱們設兩天j和k,j比k優秀的話,就會有code

$(f[j]-f[k])*a[i]+(f[j]/rate[j]-f[k]/rate[k])*b[i]>0$htm

再設$g[i]=f[i]/rate[i]$(也就是第i天得到的最多B券數),爲了處理不等式的符號咱們設$f[j]<f[k]$blog

因此有:排序

$(g[j]-g[k])*b[i]>-(f[j]-f[k])*a[i]$

$(g[j]-g[k])/(f[j]-f[k])<-a[i]/b[i]$

(固然,實際狀況是有$f[j]==f[k]$,即斜率不存在的狀況存在的,到時候還要討論。)

那麼咱們轉化到一些座標爲(f[i],g[i])的二維平面的點上來。

咱們創建一個這些點的上凸殼,而後在凸殼上二分最靠左的最後一個知足$k(point(x),point(x+1))<-a[i]/b[i]$的點x,

那麼$x+1$就是最優秀的取值,也即本次決策點。

可是你發現這個f[i]不隨i單調……那麼咱們考慮splay或者CDQ

打個J的splay啊

那麼咱們CDQ維護凸殼而且決策就行了……

具體實現是讓$f[i]$有序以後按照正常方法建凸殼,而後讓$-a[i]/b[i]$有序(我是從大到小排序),單調掃一邊完成決策。

怎麼讓這倆有序呢……一是大力sort,複雜度$O(nlog^{2}n)$,一是歸併排序,複雜度$O(nlogn)$

兩種方法我都打了一下……感受少個$logn$沒快到哪去,也沒長到哪去……

若是複雜度可行仍是打$log^{2}$吧……

兩份代碼:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define eps 1e-8
 6 #define N 100010
 7 #define db double
 8 #define inf 0x7fffffff
 9 #define sign(a) (((a)>-eps)-((a)<eps))
10 int n,top,sta[N],id[N],tmp[N],idk[N],tmpk[N],match[N];
11 db ans[N],ak[N],bk[N],rate[N],f[N],g[N];
12 inline db max(db a,db b){return a>b?a:b;}
13 inline bool comp(const int &a,const int &b)
14     {return f[a]<f[b] || ( sign(f[a]-f[b])==0&&g[a]<g[b] );}
15 inline double k(int a,int b)
16 {
17     if(sign(f[a]-f[b])==0)return sign(g[a]-g[b])*inf;
18     return (g[a]-g[b])/(f[a]-f[b]);
19 }
20 inline bool compk(const int &a,const int &b)
21     {return sign( (-ak[a]/bk[a]) - (-ak[b]/bk[b]) ) >0 ;}
22 inline void CDQ(int l,int r)
23 {
24     if(l==r){g[l]=f[l]/rate[l];return;}
25     register int hd1,i,t,mi=l+r>>1,p=l-1,q=mi,h=l-1;
26     for(i=l;i<=r;++i)
27         if(match[idk[i]]<=mi)tmpk[++p]=idk[i];
28         else tmpk[++q]=idk[i];
29     for(i=l;i<=r;++i)idk[i]=tmpk[i];
30     CDQ(l,mi);
31     for(top=0,i=l;i<=mi;++i)
32     {
33         while(top>1 && k(sta[top-1],sta[top]) < k(sta[top],id[i]) )--top;
34         sta[++top]=id[i];
35     }
36     for(hd1=1,i=mi+1;i<=r;++i)
37     {
38         t=match[idk[i]];
39         while(  hd1<top&&sign(  k(sta[hd1],sta[hd1+1]) - (-ak[t]/bk[t]) ) >=0  )++hd1;
40         ans[t]=max(ans[t],f[sta[hd1]]*ak[t]+g[sta[hd1]]*bk[t]);
41     }
42     for(i=mi+1;i<=r;++i)
43         t=match[idk[i]],ans[t]=max(ans[t],ans[t-1]),f[t]=ans[t]*rate[t]/(ak[t]*rate[t]+bk[t]);
44     CDQ(mi+1,r);
45     p=l,q=mi+1,h=l;
46     while(p<=mi&&q<=r)
47         if(comp(id[p],id[q]))tmp[h++]=id[p++];
48         else tmp[h++]=id[q++];
49     while(p<=mi)tmp[h++]=id[p++];
50     while(q<=r)tmp[h++]=id[q++];
51     for(i=l;i<=r;++i)id[i]=tmp[i];
52 }
53 int main()
54 {
55     register int i,j;
56     scanf("%d%lf",&n,&ans[1]);
57     for(i=1;i<=n;++i)
58         scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]);
59     f[1]=ans[1]*rate[1]/(ak[1]*rate[1]+bk[1]);
60     for(i=1;i<=n;++i)id[i]=idk[i]=match[i]=i;
61     sort(match+1,match+n+1,compk);
62     CDQ(1,n);
63     printf("%.3f\n",ans[n]);
64 }
nlogn
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 #define eps 1e-8
 6 #define N 100010
 7 #define db double
 8 #define inf 0x7fffffff
 9 #define sign(a) (((a)>-eps)-((a)<eps))
10 int n;
11 db ans[N],ak[N],bk[N],rate[N],f[N],g[N];
12 inline db max(db a,db b){return a>b?a:b;}
13 int top,sta[N];
14 int id[N],tmp[N],idk[N];
15 inline bool comp(const int &a,const int &b)
16 {
17     return f[a]<f[b] || ( sign(f[a]-f[b])==0&&g[a]<g[b] );
18 }
19 inline double k(int a,int b)
20 {
21     if(sign(f[a]-f[b])==0)
22         return sign(g[a]-g[b])*inf;
23     return (g[a]-g[b])/(f[a]-f[b]);
24 }
25 inline bool compk(const int &a,const int &b)
26 {
27     return sign( (-ak[a]/bk[a]) - (-ak[b]/bk[b]) ) >0 ;
28 }
29 inline void CDQ(int l,int r)
30 {
31     if(l==r){g[l]=f[l]/rate[l];return;}
32     register int hd1,i,mi=l+r>>1;
33     CDQ(l,mi);
34     for(i=mi+1;i<=r;++i)id[i]=i;sort(id+l,id+mi+1,comp);
35     for(i=mi+1;i<=r;++i)idk[i]=i;sort(idk+mi+1,idk+r+1,compk);
36     for(top=0,i=l;i<=mi;++i)
37     {
38         while(top>1 && k(sta[top-1],sta[top]) < k(sta[top],id[i]) )--top;//****
39         sta[++top]=id[i];
40     }
41     for(hd1=1,i=mi+1;i<=r;++i)
42     {
43         while(  hd1<top&&sign(  k(sta[hd1],sta[hd1+1]) - (-ak[idk[i]]/bk[idk[i]]) ) >=0  )++hd1;
44             ans[idk[i]]=max(ans[idk[i]],f[sta[hd1]]*ak[idk[i]]+g[sta[hd1]]*bk[idk[i]]);
45     }
46     for(i=mi+1;i<=r;++i)
47         ans[i]=max(ans[i],ans[i-1]),f[i]=ans[i]*rate[i]/(ak[i]*rate[i]+bk[i]);
48     CDQ(mi+1,r);
49 }
50 int main()
51 {
52     register int i,j;
53     scanf("%d%lf",&n,&ans[1]);
54     for(i=1;i<=n;++i)
55         scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]);
56     f[1]=ans[1]*rate[1]/(ak[1]*rate[1]+bk[1]);
57     for(i=1;i<=n;++i)id[i]=i;
58     CDQ(1,n);
59     printf("%.3f\n",ans[n]);
60 }
nlog^2n
相關文章
相關標籤/搜索