这题poj上时限卡得太紧了,IO不优化超市,优化IO313ms。
定义状态为f[i][j]代表走到第i行第j个点的最大高兴值,那么f[i][j]可以由两个状态转移过来,f[i][j]=max(f[i+1][k]-sum[i][k]+sum[i][j],f[i+1][k]+sum[i][k]-sum[i][j]),那么只需要计算出左边过来的最大值和右边过来的最大值即可,从上面的式子中可以发现,这是两个单调递减队列,如果新加入的元素比队尾元素大,显然要去掉队尾元素,因为队尾元素的值小并且所花的时间大,接着就是维护队头,如果队头元素到目前元素的时间大于k,那么pop掉队头元素。
需要注意一点的是在dp的时候j是可以取到0的,这个仔细想想就知道了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define ret(i,a,b) for(int i=(a);i>=(b);i--)
#define ss(x) scanf("%d",&x)
const int inf=100000000;
int sum[105][10005],ti[105][10005],f[10005],d[10005],q[10005];
int n,m,k;
int readint()
{
char c=getchar();
while(!isdigit(c)&&c!='-') c=getchar();
int q=1,tmp=0;
if(c=='-') {q=-1;c=getchar();}
while(isdigit(c)){
tmp=tmp*10+c-'0';
c=getchar();
}
return tmp*q;
}
int main()
{
while(true)
{
memset(sum,0,sizeof(sum));
memset(ti,0,sizeof(ti));
int t;
ss(n);ss(m);ss(k);if(n+m+k==0) break;
rep(i,1,n+1) rep(j,1,m) {t=readint();sum[i][j]=sum[i][j-1]+t;}
rep(i,1,n+1) rep(j,1,m) {t=readint();ti[i][j]=ti[i][j-1]+t;}
memset(f,0,sizeof(f));
ret(i,n+1,1){
rep(j,0,m) d[j]=f[j];
int front=0,rear=-1;
rep(j,0,m){
int tmp=d[j]-sum[i][j];
while(front<=rear&&tmp>=d[q[rear]]-sum[i][q[rear]]) rear--;
q[++rear]=j;
while(front<=rear&&ti[i][j]-ti[i][q[front]]>k) front++;
f[j]=max(f[j],d[q[front]]-sum[i][q[front]]+sum[i][j]);
}
front=0,rear=-1;
ret(j,m,0){
int tmp=d[j]+sum[i][j];
while(front<=rear&&d[q[rear]]+sum[i][q[rear]]<=tmp) rear--;
q[++rear]=j;
while(front<=rear&&ti[i][q[front]]-ti[i][j]>k) front++;
f[j]=max(f[j],d[q[front]]+sum[i][q[front]]-sum[i][j]);
}
}
int ans=-inf;
rep(i,1,m) ans=max(ans,f[i]);
printf("%d\n",ans);
}
return 0;
}