主要不好想到定义问题状态,一开始想枚举第一次股票交易的卖出时间【这样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 }