uva1427 单调队列优化dp

每条东西走向路有(高兴值,时间)二维参量,要从最南边走到最北边,满足:1)每条道路仅走一次2)每行道路所经过的总时间不超过k,求最大高兴值的和3)不能从北向南走

状态:f[i][j]:到达第i行第j个顶点(从左起)最大高兴值。

最短/长路问题:状态常设为以某点为起点/终点的最优值

求数组f。到达(i,j)可以从左、右、下三个方向。设辅助数组L[i][j]从左边到达(i,j)的最大高兴值,R[i][j]:从右边到达(i,j)的最大高兴值,则f[i][j]=max(f[i+1][j],L[i][j],R[i][j])

求辅助数组L与R。

L[i][j]=max{f[i+1][k]+sumvalue[i][j]-sumvalue[i][k],1<=k<=j&&sumtime[i][j]-sumtime[i][k]<=K}

       =max{funL(i,k),1<=k<=j&&sumtime[i][j]-sumtime[i][k]<=K}+sumvalue[i][j]

其中funL(i,k)=f[i+1][k]-sumtime[i][k]

R[i][j]=max{f[i+1][k]+sumvalue[i][k]-sumvalue[i][j],j<=k<=m+1&&sumtime[i][k]-sumtime[i][j]<=K}

        =max{funR(i,k),j<=k<=m+1&&sumtime[i][k]-sumtime[i][j]<=K}-sumvalue[i][j]

其中funR(i,k)=f[i+1][k]+sumvalue[i][k]

对于L和R数组的求解可以用单调队列进行优化,原理同la3983捡垃圾的机器人一题

单减队列维护满足区间条件的最大值,置于队首(top)

单增队列维护满足区间条件的最小值,置于队首(top)

单调队列的三个步骤:

1.当队首(top)元素不满足区间条件时,从队首(top)删去

2.取出当前维护的top值进行操作

3.从队尾(tail)依次删除比要加入的元素大(单增队列)/小(单减队列)的元素,最后将新元素加入单调队列


注:输入需要用字符串进行加速,否则会超时!


 #include<stdio.h>
 #include<string.h>
 #include<iostream>
 #define mem(a,b) memset(a,b,sizeof(a))
 using namespace std;

inline int GetInt() {
    char c;
    do {
        c = getchar();
    }while(!isdigit(c) && c != '-');
    int ret = 0;
    int sigma = 1;
    if(c == '-') sigma = -1;
    else ret = c - '0';
    while(isdigit(c = getchar())) ret = ret * 10 + c - '0';
    return ret*sigma;
}

 const int maxrow=200+10;
 const int maxcol=20000+10;
 const int INF = 0x3f3f3f3f;

 int n,m,K;
 int sumvalue[maxrow][maxcol],sumtime[maxrow][maxcol];
 int f[maxrow][maxcol];//第i行走到第j个顶点的最大价值
 int L[maxrow][maxcol],R[maxrow][maxcol];
 int que[maxcol];

 void prepare(){//计算第i行的走到第j个顶点的价值和与时间和
    int value,hour;
    //sumvalue[i][1]和sumtime[i][1]均为0
    for(int i=1;i<=n+1;i++){
        sumvalue[i][1]=0;
        for(int j=2;j<=m+1;j++){
            value=GetInt();
            sumvalue[i][j]=sumvalue[i][j-1]+value;
        }
    }
    for(int i=1;i<=n+1;i++){
        sumtime[i][1]=0;
        for(int j=2;j<=m+1;j++){
            hour=GetInt();
            sumtime[i][j]=sumtime[i][j-1]+hour;
        }
    }
 }

 int funL(int i,int j){//计算L[i][j]中的fun函数
     return f[i+1][j]-sumvalue[i][j];
}

int funR(int i,int j){//计算R[i][j]中的fun函数
    return f[i+1][j]+sumvalue[i][j];
}

 void fun(){//计算f[i][j]
    mem(f,0),mem(L,0),mem(R,0);
    int top,tail;
    for(int i=n+1;i>=1;i--){//第i行
        L[i][1]=f[i+1][1];
        top=tail=1,que[1]=1;
        for(int j=2;j<=m+1;j++){//第j列L数组
            while(top<=tail&&sumtime[i][j]-sumtime[i][que[top]]>K) top++;
            L[i][j]=funL(i,que[top])+sumvalue[i][j];
            //printf("L:%d %d %d\n",i,j,L[i][j]);
            while(top<=tail&&funL(i,que[tail])<=funL(i,j)) tail--;
            que[++tail]=j;
        }
        R[i][m+1]=f[i+1][m+1];
        top=tail=1,que[1]=m+1;
        for(int j=m;j>=1;j--){//第j列R数组
            while(top<=tail&&sumtime[i][que[top]]-sumtime[i][j]>K) top++;
            R[i][j]=funR(i,que[top])-sumvalue[i][j];
           // printf("R:%d %d %d\n",i,j,R[i][j]);
            while(top<=tail&&funR(i,que[tail])<=funR(i,j)) tail--;
            que[++tail]=j;
        }
        for(int j=1;j<=m+1;j++){//第j列f数组
            f[i][j]=max(f[i+1][j],max(L[i][j],R[i][j]));
        }
    }
 }

 int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&K)!=EOF,n||m||K){
       prepare();
       fun();
       int ans=-INF;
       for(int j=1;j<=m+1;j++) ans=max(ans,f[1][j]);
       printf("%d\n",ans);
    }
 }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值