每条东西走向路有(高兴值,时间)二维参量,要从最南边走到最北边,满足: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);
}
}