zoukankan      html  css  js  c++  java
  • 【HDU4374】One hundred layer-单调队列优化DP

    测试地址:One hundred layer
    题目大意:一座塔有N层,每一层有一排M个位置,要从第一层的第X个位置出发,每一层只能向某一个方向走动至多T步,然后就要跳到下一层的与该层跳下位置对应的那个位置。每个位置上都有一个分数,最终得分就是所有走过的位置上分数的和,求最大的最终得分。
    做法:本题是一个单调队列优化DP的题目。
    这一题是DP应该能很显然地看出来,设f(i,j)为从第i层第j个位置开始走,一直到达最底层的最大得分,那么有状态转移方程:
    f(i,j)=max(max{f(i+1,k)+w(i,k,j)|jTkj},max{f(i+1,k)+w(i,j,k)|jkj+T})
    其中w(i,j,k)为第i行第j到第k个位置的分数之和,即kp=jaip。这个方程应该很容易理解,里面的两个max分别表示向左走和向右走的最大得分,两者再取个max就是总体上的最大得分了。
    w可以用前缀和O(NM)预处理出来,那么上述方程就是O(NM2)的。显然会炸,需要考虑优化。
    考虑到向左走和向右走两种情况是对称的,所以我们这里只讨论向左走的情况。我们处理出了前缀和sum(i,j)=jk=1aik,那么上面的式子可以写成:
    max{f(i+1,k)+sum(i,j)sum(i,k1)|jTkj}
    我们发现sum(i,j)在求f(i,j)时可以算作常量,可以提出括号,所以我们就是要求:
    max{f(i+1,k)sum(i,k1)|jTkj}
    观察发现,f(i+1,k)sum(i,k1)是由变量k所唯一确定的常量,而k的取值范围大小恒定,显然可以使用单调队列优化,这样方程的复杂度就是O(NM)的了。
    对于向右走的情况同理,这里就不赘述了,最后算法的时间复杂度为O(NM),完美解决了这一题。
    最后还需要注意的是,输入可能有多组测试数据,小心不要被坑了……
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define inf 1000000000
    using namespace std;
    int n,m,x,t;
    int a[110][10010],f[110][10010],lsum[110][10010],rsum[110][10010];
    int q[10010],head,tail;
    
    int main()
    {
        while(scanf("%d%d%d%d",&n,&m,&x,&t)!=EOF)
        {
            memset(lsum,0,sizeof(lsum));
            memset(rsum,0,sizeof(rsum));
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    scanf("%d",&a[i][j]);
            for(int i=1;i<=m;i++) f[n+1][i]=0;
            for(int i=1;i<=n;i++)
            {
                lsum[i][0]=rsum[i][m+1]=0;
                for(int j=1;j<=m;j++) lsum[i][j]=lsum[i][j-1]+a[i][j];
                for(int j=m;j>=1;j--) rsum[i][j]=rsum[i][j+1]+a[i][j];
            }
            for(int i=n;i>=1;i--)
            {
                head=1,tail=0;
                for(int j=1;j<=m;j++)
                {
                    f[i][j]=-inf;
                    while(head<=tail&&q[head]<j-t) head++;
                    while(head<=tail&&f[i+1][j]-lsum[i][j-1]>=f[i+1][q[tail]]-lsum[i][q[tail]-1]) tail--;
                    q[++tail]=j;
                    f[i][j]=max(f[i][j],f[i+1][q[head]]+lsum[i][j]-lsum[i][q[head]-1]);
                }
                head=1,tail=0;
                for(int j=m;j>=1;j--)
                {
                    while(head<=tail&&q[head]>j+t) head++;
                    while(head<=tail&&f[i+1][j]-rsum[i][j+1]>=f[i+1][q[tail]]-rsum[i][q[tail]+1]) tail--;
                    q[++tail]=j;
                    f[i][j]=max(f[i][j],f[i+1][q[head]]+rsum[i][j]-rsum[i][q[head]+1]);
                }
            }
            printf("%d
    ",f[1][x]);
        }
    
        return 0;
    }
  • 相关阅读:
    bootstrap4 Reboot details summary 美化(点选禁止选中文本,单行隐藏角标,多行后移)
    在C#中,Newtonsoft.Json + dynamic动态解析jsonString,jsonString转实体
    在C#中,Windows Console控制台 设置控制台标题、禁用关闭按钮、关闭快速编辑模式、插入模式
    MySql数据库中,判断表、表字段是否存在,不存在就新增
    Windows 10安装Docker 步骤及顺序
    AES加密解密 助手类 CBC加密模式
    处理模板页菜单高亮
    C# 按不同的字节编码,通过字节数去截取字符串
    2018年4月13日,祝自己24岁生日快乐!
    一个数值保存复选框的值
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793581.html
Copyright © 2011-2022 走看看