UVA 1427 Parade(dp)

这题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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值