zoukankan      html  css  js  c++  java
  • HDU-3401 Trade(DP+单调队列优化)

    Trade

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 5242    Accepted Submission(s): 1810


    Problem Description
    Recently, lxhgww is addicted to stock, he finds some regular patterns after a few days' study.
    He forecasts the next T days' stock market. On the i'th day, you can buy one stock with the price APi or sell one stock to get BPi.
    There are some other limits, one can buy at most ASi stocks on the i'th day and at most sell BSi stocks.
    Two trading days should have a interval of more than W days. That is to say, suppose you traded (any buy or sell stocks is regarded as a trade)on the i'th day, the next trading day must be on the (i+W+1)th day or later.
    What's more, one can own no more than MaxP stocks at any time.

    Before the first day, lxhgww already has infinitely money but no stocks, of course he wants to earn as much money as possible from the stock market. So the question comes, how much at most can he earn?
     
    Input
    The first line is an integer t, the case number.
    The first line of each case are three integers T , MaxP , W .
    (0 <= W < T <= 2000, 1 <= MaxP <= 2000) .
    The next T lines each has four integers APi,BPi,ASi,BSi( 1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP), which are mentioned above.
     
    Output
    The most money lxhgww can earn.
     
    Sample Input
    1 5 2 0 2 1 1 1 2 1 1 1 3 2 1 1 4 3 1 1 5 4 1 1
     
    Sample Output
    3
     
    Author
    lxhgww
     
    Source
     
    Recommend
    lcy   |   We have carefully selected several similar problems for you:  3400 3403 3404 3402 3415 
     

    dp[i][j]:表示第i天手上持有j股的最大利益。分情况:

    dp[i][j]=dp[i-1][j] 不交易 
    dp[i][j]=max( dp[r][k]-ap[i]*(j-k) | j>=k,r<=i-w-1) 买进 
    dp[i][j]=max(dp[r][k]+bp[i]*(k-j) | j<=k,r<=i-w-1) 卖出 
    由于dp[i][j]不会小于之前的 dp[k][j],k < i ,所以r=i-w-1 
    这里有个可以使用单调队列的特征 
    dp[i][j]=(dp[r][k]+ap[i] * k)- ap[i] * j; 
    求dp[i][j]时只需求之前最大的dp[r][k]+ ap[i] * k,所以可以用单调队列维护。 
    卖出时同理。 

    转自http://blog.csdn.net/carryheart/article/details/52441710

    初始化须注意!

     1 #include "bits/stdc++.h"
     2 using namespace std;
     3 typedef long long LL;
     4 const int MAX=2005;
     5 int T,n,m,w;
     6 int f[MAX][MAX],ap[MAX],bp[MAX],as[MAX],bs[MAX];
     7 deque <int> q;
     8 int wo1(int i,int j){return f[i-w][j]+j*ap[i];}
     9 int wo2(int i,int j){return f[i-w][j]+j*bp[i];}
    10 int main(){
    11     freopen ("trade.in","r",stdin);
    12     freopen ("trade.out","w",stdout);
    13     int i,j;
    14     scanf("%d",&T);
    15     while (T--){
    16         scanf("%d%d%d",&n,&m,&w);w++;
    17         for (i=1;i<=n;i++) scanf("%d%d%d%d",ap+i,bp+i,as+i,bs+i);
    18         for (i=1;i<=n;i++){
    19             f[i][0]=0;
    20             for (j=1;j<=m;j++)
    21                 f[i][j]=-999999999;
    22         }
    23         for (i=1;i<=w;i++)
    24             for (j=1;j<=as[i];j++)
    25                 f[i][j]=-j*ap[i];
    26         for (i=2;i<=w;i++)
    27             for (j=1;j<=m;j++)
    28                 f[i][j]=max(f[i][j],f[i-1][j]);
    29         int zt;
    30         for (;i<=n;i++){
    31             while (!q.empty()) q.pop_back();
    32             for (j=0;j<=m;j++){
    33                 f[i][j]=max(f[i][j],f[i-1][j]);
    34                 zt=f[i-w][j]+j*ap[i];
    35                 while (!q.empty() && zt>wo1(i,q.back())) q.pop_back(); q.push_back(j);
    36                 while (!q.empty() && j-q.front()>as[i]) q.pop_front();
    37                 if (!q.empty())
    38                     f[i][j]=max(f[i][j],wo1(i,q.front())-j*ap[i]);
    39             }
    40             while (!q.empty()) q.pop_back();
    41             for (j=m;j>=0;j--){
    42                 zt=wo2(i,j);
    43                 while (!q.empty() && zt>wo2(i,q.back())) q.pop_back(); q.push_back(j);
    44                 while (!q.empty() && q.front()-j>bs[i]) q.pop_front();
    45                 if (!q.empty())
    46                     f[i][j]=max(f[i][j],wo2(i,q.front())-j*bp[i]);
    47             }
    48         }
    49         int ans=-999999999;
    50         for (i=0;i<=m;i++) ans=max(ans,f[n][i]);
    51         printf("%d
    ",ans);
    52     }
    53     return 0;
    54 }

    遇到怎样的题使用单调队列: 
    做动态规划时常常会见到形如这样的转移方程:

      f[x] = max or min{g(k) | b[x] <= k < x} + w[x]

      (其中b[x]随x单调不降,即b[1]<=b[2]<=b[3]<=…<=b[n])

      (g[k]表示一个和k或f[k]有关的函数,w[x]表示一个和x有关的函数) 
      例如上一题就是满足这样的方程,所以可以使用单调队列优化。 
      这个方程怎样求解呢?我们注意到这样一个性质:如果存在两个数j, k,使得j <= k,而且g(k) <= g(j),则决策j是毫无用处的。因为根据b[x]单调的特性,如果j可以作为合法决策,那么k一定可以作为合法决策,又因为k比j要优,(注意:在这个经典模型中,“优”是绝对的,是与当前正在计算的状态无关的),所以说,如果把待决策表中的决策按照k排序的话,则g(k)必然是不降的。

      这样,就引导我们使用一个单调队列来维护决策表。对于每一个状态f(x)来说,计算过程分为以下几步:

      1、 队首元素出队,直到队首元素在给定的范围中。

      2、 此时,队首元素就是状态f(x)的最优决策,

      3、计算g(x),并将其插入到单调队列的尾部,同时维持队列的单调性(不断地出队,直到队列单调为止)。

      重复上述步骤直到所有的函数值均被计算出来。不难看出这样的算法均摊时间复杂度是O(1)的。因此求解f(x)的时间复杂度从O(n^2)降到了O(n)。

    单调队列指一个队列中的所有的数符合单调性(单调增或单调减),在信息学竞赛的一些题目上应用,会减少时间复杂度

    未来是什么样,未来会发生什么,谁也不知道。 但是我知道, 起码从今天开始努力, 肯定比从明天开始努力, 要快一天实现梦想。 千里之行,始于足下! ——《那年那兔那些事儿》
  • 相关阅读:
    URAL 1993 This cheeseburger you don't need
    python获取教务管理系统的MM照片
    ZOJ 3175 Number of Containers 分块
    ZOJ 3435 Ideal Puzzle Bobble 莫比乌斯反演
    整理各种线性筛法
    SPOJ 7001 Visible Lattice Points 莫比乌斯反演
    UVA 11997 K Smallest Sums
    HDU 4768 Flyer 二分
    HDU 4135 Co-prime 容斥
    POJ 3468 A Simple Problem with Integers 线段树
  • 原文地址:https://www.cnblogs.com/keximeiruguo/p/7683622.html
Copyright © 2011-2022 走看看