有一个人要从最下边走到最上边,只能往上走不能往下走,在同一层只能往左或往右,不能来回走,每层上每个横着的边都有一个花费和一个收益,要求在一层上的花费不能超过k,可以不再这一层走直接走向下一层,求最大收益。
直接DP,状态为dp[i][j]表示从最下边走到第i层的第j列,且已经在第i层走过了,即不会再在第i层上走了的最大收益。
这样是n*m的状态,每个状态是m的转移,可以用单调队列优化掉转移的复杂度,变成均摊O(1)的转移。
#include <cstdio>
#include <cstring>
struct QueNode {
int v,x;
void clear(int vv,int xx) {
v=vv;x=xx;
}
};
int n,m,k;
int a[112][10011];
int b[111][10011];
int dp[111][10011];
QueNode que[10011];
int p,q;
int sum,x;
inline int in() {
char c=getchar();
while ((c<'0'||c>'9')&&c!='-') c=getchar();
bool flag=false;
if (c=='-') {
flag=true;
c=getchar();
}
int ans=0;
while (c>='0'&&c<='9') {
ans=ans*10+c-'0';
c=getchar();
}
//printf("%d\n",ans);
if (flag) return -ans;
return ans;
}
int main() {
int i,j;
while (scanf("%d%d%d",&n,&m,&k),n!=0) {
for (i=0;i<=n;i++)
for (j=0;j<m;j++)
a[i][j]=in();
for (i=0;i<=n;i++)
for (j=0;j<m;j++)
b[i][j]=in();
for (j=0;j<=m;j++) dp[n+1][j]=0;
for (i=n;i>=0;i--) {
x=sum=p=q=0;
dp[i][0]=dp[i+1][0];
que[q++].clear(dp[i+1][0],0);
for (j=1;j<=m;j++) {
x+=b[i][j-1];
sum+=a[i][j-1];
int tmp=dp[i+1][j]-sum;
while (p<q&&que[q-1].v<=tmp) q--;
while (p<q&&x-que[p].x>k) p++;
que[q++].clear(tmp,x);
dp[i][j]=que[p].v+sum;
}
x=sum=p=q=0;
que[q++].clear(dp[i+1][m],0);
for (j=m-1;j>=0;j--) {
x+=b[i][j];
sum+=a[i][j];
int tmp=dp[i+1][j]-sum;
while (p<q&&que[q-1].v<=tmp) q--;
while (p<q&&x-que[p].x>k) p++;
que[q++].clear(tmp,x);
if (dp[i][j]<que[p].v+sum)
dp[i][j]=que[p].v+sum;
}
}
int ans=0;
for (j=0;j<=m;j++)
if (dp[0][j]>ans) ans=dp[0][j];
/*
for (i=0;i<=n;i++) {
for (j=0;j<=m;j++)
printf("%d ",dp[i][j]);
printf("\n");
}
*/
printf("%d\n",ans);
}
return 0;
}