hdu4374 One hundred layer(单调队列维护dp)

190 篇文章 2 订阅
84 篇文章 2 订阅

题面

题意

给出宽n,长m的矩阵,你一开始在第一行第k个,在每行你最多向左或向右移动t步(在底层和顶层皆可移动),移动后进入下一层,问到达底层后的最大分数是多少.

方法

用dp思想可知,只需要知道到达每一点的最大得分是多少,若穷举每行的起点和终点进行状态转移,复杂度为O(n*m^2)必然TLE.
其实在第i行若终点为j,则起点必然在max(1,j-t)与min(m,j+t)之间,对于此类求区间最大值的题目当用单调队列来维护,则复杂度为O(n*m).

也可以这么想:
不难得到状态转移方程:
在第i行中,k为起点,j为终点,qz表示前缀和
j>=k时
dp[i][j]=dp[i-1][k]+qz[i][j]-qz[i][k-1] ->
dp[i][j]-qz[i][j]=dp[i-1][k]-qz[i][k-1]
j<=k时
dp[i][j]=dp[i-1][k]+qz[i][k]-qz[i][j-1]->
dp[i][j]+qz[i][j-1]=dp[i-1][k]+qz[i][k]
因而只需用单调队列求出等号右边的最小值

代码

#include<bits/stdc++.h>
#define ll long long
#define M 110
#define N 10100
#define MN -100000000
using namespace std;

ll m,n,k,l,num[M][N],qz[M][N],last[N],now,dp[M][N],ans;
struct Dq
{
    ll nu[N],id[N],head,tail,len;
    void init()
    {
        head=1;
        tail=0;
    }
    void push(ll u,ll v)
    {
        if(head<=tail&&v-id[head]>=len) head++;
        while(head<=tail&&u>=nu[tail]) tail--;
        tail++;
        nu[tail]=u;
        id[tail]=v;
    }
    ll front()
    {
        return nu[head];
    }
    ll front2()
    {
        return id[head];
    }
    void pop()
    {
        head++;
    }
    bool empty()
    {
        if(head>tail) return 1;
        return 0;
    }
    void getlen(ll u)
    {
        len=u;
    }
    void print()
    {
        ll i;
        for(i=head;i<=tail;i++)
        {
            cout<<nu[i]<<" ";
        }
        cout<<endl;
    }
    void print2()
    {
        ll i;
        cout<<head<<" "<<tail<<endl;
        for(i=head;i<=tail;i++)
        {
            cout<<nu[i]<<" ";
        }
        cout<<endl;
    }
};
Dq dq;

int main()
{
    ll i,j;
    while(~scanf("%lld%lld%lld%lld",&m,&n,&k,&l))
    {
        dq.getlen(l+1);
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%lld",&num[i][j]);
            }
        }
        for(i=1;i<=m;i++)
        {
            qz[i][1]=num[i][1];
            for(j=2;j<=n;j++)
            {
                qz[i][j]=qz[i][j-1]+num[i][j];
            }
        }
        for(i=1;i<=n;i++) dp[1][i]=MN;
        dp[1][k]=num[1][k];
        for(i=k+1;i<=min(k+l,n);i++) dp[1][i]=dp[1][i-1]+num[1][i];
        for(i=k-1;i>=max((ll)1,k-l);i--) dp[1][i]=dp[1][i+1]+num[1][i];
        for(i=2;i<=m;i++)
        {
            //left
            dq.init();
            for(j=1;j<=n;j++)
            {
                dq.push(dp[i-1][j]+qz[i][j],j);
                if(j>l) dp[i][j-l]=dq.front()-qz[i][j-l-1];
            }
            for(j=1;j<=min(l,n);j++)
            {
                while(!dq.empty()&&dq.front2()<n-l+j) dq.pop();
                dp[i][n-l+j]=dq.front()-qz[i][n-l+j-1];
            }
            //right
            dq.init();
            for(j=n;j>=1;j--)
            {
                dq.push(dp[i-1][j]-qz[i][j-1],n-j+1);
                if(j<=n-l) dp[i][j+l]=max(dp[i][j+l],dq.front()+qz[i][j+l]);
            }
            for(j=1;j<=l;j++)
            {
                while(!dq.empty()&&n-dq.front2()+1>l-j+1) dq.pop();
                dp[i][l-j+1]=max(dp[i][l-j+1],dq.front()+qz[i][l-j+1]);
            }
        }
        ans=MN;
        for(i=1;i<=n;i++)
        {
            ans=max(ans,dp[m][i]);
        }
        printf("%lld\n",ans);
    }
}


/*
right :
           dp[i][j]=dp[i-1][k]+qz[i][j]-qz[i][k-1]
  dp[i][j]-qz[i][j]=dp[i-1][k]-qz[i][k-1]
left :
           dp[i][j]=dp[i-1][k]+qz[i][k]-qz[i][j-1]
dp[i][j]+qz[i][j-1]=dp[i-1][k]+qz[i][k]
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值