Hdu3401(dp+单调队列)

题目大意:

一个人有T元,最大可以持有的股票数量是maxp,交易必须隔w天进行.告诉每天可以买股票的数量和买入价格,以及每天可以卖出股票的数量和卖出价格,问最后最大的收益是多少.

思路:

状态可以定义为:f[i][j]第i天,手持j股且完成当天的操作的最大收益.考虑这一天的操作,一共有三种,(1)什么都不做,(2)在这一天,有一次卖出交易,(3)在这一天,有一次买入交易.那么也就是f[i][j]=max(f[i-1][j],f[i-w-1][k]+(j-k)*Bpi,f[i-w-1][k]-(k-j)*Api)

两个疑问.

第一,为什么在当前天只考虑跟这天恰好隔w天的,相隔最少是w天,也有可能相隔为w+x天啊.那么假设我们考虑相隔w,w+1,w+2,w+3...的这些天,那么实际上考虑了w+1天的时候就已经考虑了这一天什么都不做的形式了,也就是说已经考虑了f[i-1][j]的形式了,所以在每一天都考虑f[i-1][j]的话,就不需要考虑每一个大于w的间隔.因为已经考虑过了.

第二,这样看来状态数是O(2000*2000) ,而转移数(枚举k)是O(2000),整体复杂度为(8*10^9) 不能接受.如何优化?如果可以在O(1)的时间内找到最大的买入卖出的状态的话就可以接受了.对状态进行变形.f[i-w-1][k]+(j-k)*Bpi=f[i-w-1][k]-k*Bpi+j*Bpi.针对给定的要求状态f[i][j],后面的j*Bpi是一个常数,而前面的f[i-w-1][k]-k*Bpi前一维i-w-1针对一个给定的i是一个常量,后一维k是小于j的数.那么如果能快速求出f[i-w-1][k]-k*Bpi的最大值就ok了.因为只要i确定,i-w-1就唯一确定,而且求解的顺序是一定的,那么如果现在的i确定了,我们判断它是否大于w+1,如果大于,也就是说,我们在枚举每一个j的过程中都会用到f[i-w-1][k] (k<j).在和j并列的地方先枚举k,同时用单调队列维护最大值,然后在枚举j,过程中单调队列平摊O(1)的复杂度可以快速的给出对于一个指定的j相应的那个k使得f[i-w-1][k]最大.

AC代码.

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 #define maxn 2010
 6 #define INF 0x7fffffff
 7 struct node{
 8    int x; //单调队列的dp[i-w-1][k]+APi[i]*k或dp[i-w-1][k]+BPi[i]*k
 9    int p; //持股数
10 }que[maxn],temp;
11 int s,e,dp[maxn][maxn];
12 int main()
13 {
14     //freopen("in.txt","r",stdin);
15     int Api[maxn],Bpi[maxn],Asi[maxn],Bsi[maxn],T,Maxp,W,t,res;
16     int i,j;
17     scanf("%d",&t);
18     while(t--)
19     {
20         res=-INF;
21         scanf("%d%d%d",&T,&Maxp,&W);
22         for(i=1;i<=T;i++)
23          scanf("%d%d%d%d",&Api[i],&Bpi[i],&Asi[i],&Bsi[i]);
24         for(i=0;i<=T;i++)
25          for(j=0;j<=Maxp;j++)
26           dp[i][j]=-INF;
27         for(i=1;i<=W+1;i++)
28          for(j=0;j<=Asi[i];j++)
29           dp[i][j]=-Api[i]*j;
30         for(i=2;i<=T;i++)
31         {
32          for(j=0;j<=Maxp;j++)
33            dp[i][j]=max(dp[i][j],dp[i-1][j]);
34          if(i<=W+1) continue;
35          //买入
36          s=e=1;
37          for(j=0;j<=Maxp;j++)
38          {
39             temp.x=dp[i-W-1][j]+j*Api[i];
40             temp.p=j;
41             for(;s<e&&que[e-1].x<temp.x;e--);
42             que[e++]=temp;
43             for(;s<e&&que[s].p+Asi[i]<j;s++);
44             dp[i][j]=max(dp[i][j],que[s].x-Api[i]*j);
45          }
46          //卖出
47          s=e=1;
48          for(j=Maxp;j>=0;j--)
49          {
50             temp.x=dp[i-W-1][j]+j*Bpi[i];
51             temp.p=j;
52             for(;s<e&&que[e-1].x<temp.x;e--);
53             que[e++]=temp;
54             for(;s<e&&que[s].p-Bsi[i]>j;s++);
55             dp[i][j]=max(dp[i][j],que[s].x-Bpi[i]*j);
56          }
57         }
58         for(i=0;i<=Maxp;i++)
59          res=max(res,dp[T][i]);
60         cout<<res<<endl;
61     }
62     return 0;
63 }

时间: 2024-10-25 05:05:41

Hdu3401(dp+单调队列)的相关文章

习题:烽火传递(DP+单调队列)

烽火传递[题目描述]烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上.一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情.在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价.为了使情报准确的传递,在m个烽火台中至少要有一个发出信号.现输入n.m和每个烽火台发出的信号的代价,请计算总共最少需要花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递!!![输入描述]第一行有两个数n,m(1<=n,m<=1000000)分别表示n个烽火台

poj3017 dp+单调队列

http://poj.org/problem?id=3017 Description Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of

hdu3401 Trade(单调队列优化dp)

Trade Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4734    Accepted Submission(s): 1587 Problem Description Recently, lxhgww is addicted to stock, he finds some regular patterns after a few d

hdu3401:单调队列优化dp

第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次交易后至少要间隔w天才能再次交易,初始有0股,本金无限,求最大收益 题解:dp[i][j]表示第 i 天,有 j 股的最大收益 状态转移 dp[i][j]=max{dp[i-1][j](不买不卖),dp[r][k]-(j-k)*pa[i](i-r>w,j-k<=na[i],买),dp[r][k]+

12170 - Easy Climb(DP+单调队列)

该题需要用数据结构来优化DP ,具体方法就是之前第八章讲的(用数据结构优化算法,紫书P241),使用一个数组和两个指针维护一个单调队列, 可以在O(n)的时间内求出滑动窗口中的最小值 . 有了这个优化我们就可以快速的求出dp[i-1][j](x-d<=j<=x+d)的最小值. 然而刘汝佳就是不这么做,他只用了一个指针,连维护优先队列的数组都没开,就"隐式的"求出了最小值 . 具体做法是: 1.先维护窗口左边界,别让指针k超出了窗口,如果x[k] < x[j] - d那

bzoj2500: 幸福的道路(树形dp+单调队列)

好题.. 先找出每个节点的树上最长路 由树形DP完成 节点x,设其最长路的子节点为y 对于y的最长路,有向上和向下两种情况: down:y向子节点的最长路g[y][0] up:x的次长路的g[x][1]+dis[x][y] up:up[fa[x]]+dis[x][y] dfs1找向下,即向子节点的最长路 dfs2找向上的最长路 最后最长路f[i]=max(up[x],g[x][0]) 第二部分 找最长连续子序列,使得序列中abs(mx-mn)<=m 这次学习了用单调队列的做法 两个队列mx,mn

UESTC 594 我要长高 dp单调队列优化入门

//其实是个伪单调队列...渣渣刚入门 //戳这里:594 //dp[ i ][ j(现身高) ] = min(    dp[ i ][ k(现身高) ]  + fabs( j(现身高) - k(现身高) ) * C + ( j(现身高) - h[i](原身高) )  *( j(现身高) - h[i](原身高) )     ); 观察到可以单调队列优化,O(N * H * H)  —>  O(N * H) j >= k 时, dp[ i ][ j ] = min (    dp[ i ][ k

【烽火传递】dp + 单调队列优化

题目描述 烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上.一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情.在某两座城市之间有 n 个烽火台,每个烽火台发出信号都有一定的代价.为了使情报准确的传递,在 m 个烽火台中至少要有一个发出信号.现输入 n.m 和每个烽火台发出的信号的代价,请计算总共最少需要多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递! 输入格式 第一行有两个数 n,m 分别表示 n 个烽火台,在任意连续的 m 个烽火台中至少

BZOJ 2500 幸福的道路 树形DP+单调队列

题目大意:给定一棵树,令a[i]为从第i个节点出发的最长链,求a[i]中最长的区间,满足区间内最大值与最小值之差不超过m 读错题害死人,脑残害死人 求a[i]显然是树形DP 考虑从一个点出发的链可以从子节点走,也可以从父节点走 因此我们DP两次,第一次求出从子节点走的最长链,第二次求出从父节点走的最长链,两次取max就是答案 但是直接DP会有问题,因为从父节点走的最长链可能是从自己的子树出发的,这样就会走重 因此除记录从子节点出发的最长链外还要记录一个从另一个子节点出发的次长链,如果最长链长度相