POJ 8464 股票买卖 【dp】【北大ACM/ICPC竞赛训练】

 主要不好想到定义问题状态,一开始想枚举第一次股票交易的卖出时间【这样O(1)处理第一次买入时间,logN处理第二次最大利益(第二次交易实际上是取最大值,所以可以用堆维护)】,但T*N*logN就tle了。

正解是dp1(i)代表1-i天一次交易的最大收益;再dp2(i)代表i-n天的最大收益。

以dp1(i)  = max( dp1(i-1) , a[i]-prev[i] ),prev是最小前缀,dp2同理。

这样O(n)做完。

难点在于怎么定义这个状态,因为一般来说会像我那么想i得是能提供一些信息的东西,而这里的dp(i)却不代表一定要在第i天做什么。所以这里的动态规划融合了【分治】的思想,要交易两次是吧,而且两次的时间不能有重叠,所以我就分开看。所以我们是从答案长什么样子入手,知道他们是分开的。那么一定能写成1-i天最大收益+i-n天最大收益的形式(不然他就不是最优解了),由此定义出的状态。所以像我之前对于dp的理解一样,我们定义状态如果直接定义出来了那么很好,不然的话要得到一些性质,并从性质入手去描述状态。

===2018.9.6===

又看了一遍这题,这题算是陪伴了我时间最久的题之一了啊。记得刚学dp的时候就是这题死活做不出来,直到暑假ACM课上做会,再到现在重新看。

虽然这一次看还是没做出来。。

主要是因为这题不是常规dp题,比较难想。

 1 #include<iostream>
 2 using namespace std;
 3 
 4 int a[100005],Prev[100005],post[100005],dp1[100005],dp2[100005];
 5                                         //dp1[i] 1-i的最大收益
 6                                         //dp2[i] i-n的最大收益 
 7 int main(){
 8     int t; cin>>t;
 9     while(t--){
10         int n,ans=0; cin>>n;
11         for(int i=1;i<=n;i++) scanf("%d",a+i);
12         for(int i=1;i<=n;i++) dp1[i]=dp2[i]=0;
13     
14         Prev[1]=a[1]; for(int i=2;i<=n;i++) Prev[i]=min(Prev[i-1],a[i]);//在第i天能买股票的最低价
15         post[n]=a[n]; for(int i=n-1;i>=1;i--) post[i]=max(post[i+1],a[i]);//第i天到第n天能卖出的最大值 
16 
17         for(int i=2;i<=n;i++) dp1[i]=max(dp1[i-1],a[i]-Prev[i]);
18         for(int i=n-1;i>=1;i--) dp2[i]=max( dp2[i+1],post[i]-a[i] );
19         for(int i=1;i<=n;i++) ans=max(ans,dp1[i]+dp2[i]);
20         
21         cout<<ans<<endl;
22     }
23     
24     return 0;
25 }
原文地址:https://www.cnblogs.com/ZhenghangHu/p/9359307.html