现在的位置: 首页 > 综合 > 正文

hdu4374 One hundred layer

2014年09月05日 ⁄ 综合 ⁄ 共 4273字 ⁄ 字号 评论关闭

单调队列优化dp

单调队列可以有两个操作:

1、插入一个新的元素,该元素从队尾开始向队首进行搜索,找到合适的位置插入之,如果该位置原本有元素,则替换它。

2、在过程中从队首删除不符合当前要求的元素。

 

单调队列实现起来可简单,可复杂。简单的一个数组,一个head,一个tail指针就搞定。复杂的用双向链表实现。

 

用处:

1、保存最优解,次优解,ect。

2、利用单调队列对dp方程进行优化,可将O(n)复杂度降至O(1)。也就是说,将原本会超时的N维dp降优化至N-1维,以求通过。这也是我想记录的重点

是不是任何DP都可以利用单调队列进行优化呢?答案是否定的。

记住!只有形如 dp[i]=max/min (f[k]) + g[i]  (k<i && g[i]是与k无关的变量)才能用到单调队列进行优化。(降维实质:把状态转移方程分为两部分:只与k有关,只与i有关。 与k有关的状态在前i-1次压入que,i有关的是此次状态转移所需的g[i])

优化的对象就是f[k]。

One hundred layer

Problem Description
Now there is a game called the new man down 100th floor. The rules of this game is:
  1.  At first you are at the 1st floor. And the floor moves up. Of course you can choose which part you will stay in the first time.
  2.  Every floor is divided into M parts. You can only walk in a direction (left or right). And you can jump to the next floor in any part, however if you are now in part “y”, you can only jump to part “y” in the next floor! (1<=y<=M)
  3.  There are jags on the ceils, so you can only move at most T parts each floor.
  4.  Each part has a score. And the score is the sum of the parts’ score sum you passed by.
Now we want to know after you get the 100th floor, what’s the highest score you can get.
 


Input
The first line of each case has four integer N, M, X, T(1<=N<=100, 1<=M<=10000, 1<=X, T<=M). N indicates the number of layers; M indicates the number of parts. At first you are in the X-th part. You can move at most T parts in every floor in
only one direction.
Followed N lines, each line has M integers indicating the score. (-500<=score<=500)
 


Output
Output the highest score you can get.
 


Sample Input
3 3 2 1 7 8 1 4 5 6 1 2 3
 


Sample Output
29
#include<iostream>  //是男人就下100层   N层  各M阶 起始1层X位置 至多K步
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100;
const int maxm=10000;
#define inf 1000000000
int n,m,X, k;
int s[maxn+5][maxm+5];
int dp[maxn+5][maxm+5];
int que[maxm*10+5],idx[maxm*10+5],head,tail;
int sol() {
    for(int i = 0; i <= n; i++) {
        for(int j = 0; j <= m; j++) {
            dp[i][j] = -inf;
        }
    }
    dp[0][X] = 0;
    for(int i=1;i<=n;i++) {
        head=1,tail=0;
        int ss=0;
        for(int j=1;j<=m;j++) {
            int temp=dp[i - 1][j]-ss;
            while(head<=tail&&que[tail]<=temp) tail--;
            que[++tail]=temp;
            idx[tail]=j;
            while(j-idx[head]>k) head++;
            ss += s[i][j];
            if(head<=tail&&que[head]+ss>dp[i][j]) dp[i][j]=que[head]+ss;
        }
        
        head=1,tail=0;
        
        ss=0;
        for(int j=m;j>=1;j--) {
            int temp=dp[i - 1][j]-ss;
            while(head<=tail&&que[tail]<=temp) tail--;
            que[++tail]=temp;
            idx[tail]=j;
            while(idx[head]-j > k) head++;
            ss+=s[i][j];
            if(head<=tail&&que[head]+ss>dp[i][j]) dp[i][j]=que[head]+ss;
        }
    }
    int maxx = -inf;
    for(int i = 1; i <= m; i++) maxx = max(maxx, dp[n][i]);
    return maxx;
}
int main() {
    while(~scanf("%d%d%d%d",&n,&m, &X, &k)) {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&s[i][j]);
        printf("%d\n",sol());
    }        
}
/*
2 2 2 1
-1 -2
-1 -2
*/

还有一个比较适合理解该优化方法的题目是HDU 3401http://acm.hdu.edu.cn/showproblem.php?pid=3401

大概题目便是:一个人知道接下来T天的股市行情,想知道最终他能赚到多少钱。

构造状态dp[i][j]表示第i 天拥有 j只股票的时候,赚了多少钱

状态转移有:

1、从前一天不买不卖:

dp[i][j]=max(dp[i-1][j],dp[i][j])

2、从前i-W-1天买进一些股:

dp[i][j]=max(dp[i-W-1][k]-(j-k)*AP[i],dp[i][j])

3、从i-W-1天卖掉一些股:

dp[i][j]=max(dp[i-W-1][k]+(k-j)*BP[i],dp[i][j])

这里需要解释一下为什么只考虑第i-W-1天的买入卖出情况即可。想想看,i-W-2天是不是可以通过不买不卖将自己的最优状态转移到第i-W-1天?以此类推,之前的都不需要考虑了,只考虑都i-W-1天的情况即可。

 

对买入股票的情况进行分析,转化成适合单调队列优化的方程形式

dp[i][j]=max(dp[i-W-1][k]+k*AP[i])-j*AP[i]。令f[i-W-1][k]=dp[i-W-1][k]+k*AP[i],则dp[i][j]=max(f[i-W-1][k]) - j*AP[i]。

这便可以用单调队列进行优化了。卖股的情况类似分析。

 #include<iostream>
 #include<string>
 using namespace std;
 #define inf 0xfffffff
 #define min(a,b) a<b?a:b
 #define max(a,b) a>b?a:b
 
 struct node
 {
     int pos;
     int f;
 };
 
 int AP[2001],BP[2001],AS[2001],BS[2001];
 int dp[2001][2001];
 node q[2001];
 int head,tail;
 int n,MaxP,W;
 
 int main()
 {
     int i,j,cas,nowf;
     freopen("D:\\in.txt","r",stdin);
     scanf("%d",&cas);
     while(cas--)
     {
         scanf("%d%d%d",&n,&MaxP,&W);
         for(i=1;i<=n;i++)
         {
             scanf("%d%d%d%d",&AP[i],&BP[i],&AS[i],&BS[i]);
         }
         for(i=0;i<=n;i++)
             for(j=0;j<=MaxP;j++)
                 dp[i][j]=-inf;
         for(i=1;i<=W+1;i++)
         {
             for(j=0;j<=(min(MaxP,AS[i]));j++)
                 dp[i][j]=-AP[i]*j;
         }
         dp[0][0]=0;
         for(i=1;i<=n;i++)
         {
             for(j=0;j<=MaxP;j++)
                 dp[i][j]=max(dp[i][j],dp[i-1][j]);
             if(i<=W+1)
                 continue;
             int pre=i-W-1; //队列里保存的是第i-W-1天的信息
             head=tail=0;
             for(j=0;j<=MaxP;j++) //因为是买,所以肯定越来越多,求DP[i][j]的时候,之前的0~~j-1的信息就存在在队列了
             {
                 nowf=dp[pre][j]+j*AP[i];
                 while(head<tail && q[tail-1].f<nowf)
                     tail--;
                 q[tail].f=nowf;q[tail++].pos=j;
                 while(head<tail && q[head].pos+AS[i]<j)
                     head++;
                 dp[i][j]=max(dp[i][j],q[head].f-j*AP[i]);
             }
             head=tail=0;
             for(j=MaxP;j>=0;j--) //因为是卖,道理同上
             {
                 nowf=dp[pre][j]+j*BP[i];
                 while(head<tail && q[tail-1].f<nowf)
                     tail--;
                 q[tail].f=nowf;q[tail++].pos=j;
                 while(head<tail && q[head].pos-BS[i]>j)
                     head++;
                 dp[i][j]=max(dp[i][j],q[head].f-j*BP[i]);
             }
         }
         int ans=0;
         for(i=0;i<=MaxP;i++)
             ans=max(ans,dp[n][i]);
         printf("%d\n",ans);
     }
     return 0;
 }

抱歉!评论已关闭.