单调队列 hdu3401 Trade

来源:互联网 发布:ug轮廓3d倒角编程技巧 编辑:程序博客网 时间:2024/04/30 11:37

传送门:点击打开链接

题意:有种股票,现在知道n天,拥有的股票股数必须<=V,每两次操作时间之差必须大于W。接下来n天,分别是当天股票买和卖的价格,以及买和卖的最大上限。刚开始认为钱是INF的,问最后能赚多少钱。

思路:一道非常好的单调队列优化dp的好题!

首先,dp无非就两种设法,

1.dp[i][j]表示最后一次操作为第i天,已经拥有了j只股票的赚的最大钱数.

2.dp[i][j]前i天里,已经拥有了j只股票的赚的最大钱数.


我们可以发现,如果是第一种设法,方程不好转移,所以我们采用第二种,那么很容易就能列出方程

对于i>=W+2
dp[i][j]=dp[i-1][j]
买股票 dp[i][j]=max(dp[i][j],dp[i-W-1][k]+k*AP[i])-j*AP[i],  j-k<=AS[i]
卖股票 dp[i][j]=max(dp[i][j],dp[i-W-1][k]+k*BP[i])-j*BP[i],  j-k<=BS[i]
所以,买股票的时候j的循环应该是从小到大,卖股票的时候循环应该是从大到小。

然后我们继续来考虑i>=W+2的时候,对于单调队列添加起点的边界考虑。
买股票的时候,我们把j=0添加到单调队列中,卖股票的时候,我们把j=V添加到单调队列中

对于比较复杂的方程,有时候单纯的用单调队列只记录位置,就会显得有点笨重,这里我是采用了结构体,这样可以使得代码更加清晰,而且方便维护。

这题还有另外一个边界,那就是1<=i<=W+1的情况
dp[i][j]=j<=AS[i]?-j*AP[i]:-INF;
if(i>1) dp[i][j]=max(dp[i][j],dp[i-1][j]);
这个怎么理解呢,因为当i在这个区间的时候,肯定只能取到某一天。
然后对于j>AS[i]的情况,因为第i天实在买不了j只股票,所以这个状态是达不到的,我们记为-INF
其次就是最容易错的那句if语句,因为我们的dp的意义,并不是以第i天为最后一天操作时的赚的最大钱数,而是前i天!
所以我们应该还要考虑这天不选的情况,所以还应该考虑只从前面的状态转移过来时的情况

总的来说,我觉得这题有几个地方都很适合总结。
一个是dp的两种设法,以后要习惯性的想到,不能死扣一种。
第二个就是关于dp的边界,一定要结合dp的含义想清楚再写,否则很有可能调试不出来。
第三个就是单调队列的初始边界,一般是把第一个状态直接加入进去,然后for循环从第二个状态开始
#include<map>#include<set>#include<cmath>#include<ctime>#include<stack>#include<queue>#include<cstdio>#include<cctype>#include<string>#include<vector>#include<cstring>#include<iostream>#include<algorithm>#include<functional>#define fuck(x) cout<<"["<<x<<"]"#define FIN freopen("input.txt","r",stdin)#define FOUT freopen("output.txt","w+",stdout)using namespace std;typedef long long LL;typedef pair<int, int>PII;const int MX = 2e3 + 5;const int INF = 0x3f3f3f3f;int n, V, W;int dp[MX][MX];int c, r;int AP[MX], BP[MX], AS[MX], BS[MX];void umax(int &a, int b) {    a = a > b ? a : b;}struct Data {    int id, x;    Data() {}    Data(int _id, int _x) {        id = _id; x = _x;    }} Q[MX];int solve() {    for(int i = 1; i <= W + 1; i++) {        for(int j = 0; j <= V; j++) {            dp[i][j] = j <= AS[i] ? -j * AP[i] : -INF;            if(i > 1) umax(dp[i][j], dp[i - 1][j]);        }    }    for(int i = W + 2; i <= n; i++) {        for(int j = 0; j <= V; j++) {            dp[i][j] = dp[i - 1][j];        }        c = r = 0; Q[r++] = Data(0, dp[i - W - 1][0]);        for(int j = 1; j <= V; j++) {            while(c < r && j - Q[c].id > AS[i]) c++;            int Max =  Q[c].x;            umax(dp[i][j], Max - j * AP[i]);            Data temp(j, dp[i - W - 1][j] + j * AP[i]);            while(c < r && Q[r - 1].x < temp.x) r--;            Q[r++] = temp;        }        c = r = 0; Q[r++] = Data(V, dp[i - W - 1][V] + V * BP[i]);        for(int j = V - 1; j >= 0; j--) {            while(c < r && Q[c].id - j > BS[i]) c++;            int Max = Q[c].x;            umax(dp[i][j], Max - j * BP[i]);            Data temp(j, dp[i - W - 1][j] + j * BP[i]);            while(c < r && Q[r - 1].x < temp.x) r--;            Q[r++] = temp;        }    }    return dp[n][0];}int main() {    int T; //FIN;    scanf("%d", &T);    while(T--) {        scanf("%d%d%d", &n, &V, &W);        for(int i = 1; i <= n; i++) {            scanf("%d%d%d%d", &AP[i], &BP[i], &AS[i], &BS[i]);        }        printf("%d\n", solve());    }    return 0;}


0 0
原创粉丝点击