Live Archive 4327 Parade

题意:某城由n+1个横向路和m+1个竖向路组成,你的任务是从最南边的路走到最北边的路,使得走过的路上高兴值最大,同一条路不能经过两次,也不能从北向南走,另外,每条横向路有一个长度,在每一行的横向路行走的长度不能超过k

简单起见我们可以看成从北边走到南边

首先预处理数组sum[i][j]表示第i行从第1个点到第j个点的高兴值总和

用f[i][j]表示从i-1行往下走一行位于(i且,j)点时得到的最大的高兴值,我们先不考虑长度的限制,那么容易得到以下方程:

f[i][j]=max{f[i-1][k]+sum[i-1][max(j,k)]-sum[i-1][min(j,k)]}

由于sum[i-1][j]是一个相对常量,所以求f[i-1][k]+sum[i-1][k]的最大值(或求f[i-1][k]-sum[i-1][k]的最大值)要用到单调队列

那么有长度限制该怎么办呢?我们可以再预处理两个数组l[i],r[i]

l[i][j]表示在第i排从第j号点开始向左走能到达的最远的点(即这两个点之间的路的长度和<=k)

那么r[i][j]同理

使用g[j]=f[i-1][k]±sum[i-1][k],然后单调队列里记录编号,如果编号小于l[i][j]或者大于r[i][j]就将队首元素弹出

注意最后答案是max{f[n+2][i]}而不是max{f[n+1][i]}想一想为什么

#include<cstdio>
#include<iostream>
#include<deque>
using namespace std;
const int maxn=105,maxm=10005,inf=1e9;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,m,k,sum[maxn][maxm],t[maxn][maxm],g[maxm];
int f[maxn][maxm],l[maxn][maxm],r[maxn][maxm];
deque<int>q;
int main(){
	while(scanf("%d%d%d",&n,&m,&k)&&(n||m||k)){
		int i,j,x;
	    for(i=1;i<=n+1;i++)
	    	for(j=1;j<=m;j++){
	        	_read(x);
	        	sum[i][j+1]=sum[i][j]+x;
			}
		for(i=1;i<=n+1;i++)
		    for(j=1;j<=m;j++)_read(t[i][j]);
	    for(i=1;i<=n+1;i++){
	    	int cur=0,id=1;
	    	l[i][1]=1,r[i][m+1]=m+1;
	    	for(j=2;j<=m+1;j++){
	    		cur+=t[i][j-1];
	    		while(cur>k)cur-=t[i][id++];
	    		l[i][j]=id;
			}
			cur=0,id=m;
			for(j=m;j;j--){
				cur+=t[i][j];
				while(cur>k)cur-=t[i][id--];
				r[i][j]=id+1;
			}
		}
	    for(i=1;i<=m+1;i++)f[n+1][i]=0;
	    for(i=2;i<=n+2;i++){
	    	while(!q.empty())q.pop_back();
	    	for(j=1;j<=m+1;j++){
	    	    g[j]=f[i-1][j]-sum[i-1][j];
	    		while(!q.empty()&&q.front()<l[i-1][j])q.pop_front();
	    		while(!q.empty()&&g[j]>=g[q.back()])q.pop_back();
	    		q.push_back(j);
	    		f[i][j]=max(f[i-1][j],sum[i-1][j]+g[q.front()]);
			}
			while(!q.empty())q.pop_back();
			for(j=m+1;j;j--){
				g[j]=f[i-1][j]+sum[i-1][j];
				while(!q.empty()&&q.front()>r[i-1][j])q.pop_front();
				while(!q.empty()&&g[j]>=g[q.back()])q.pop_back();
				q.push_back(j);
				f[i][j]=max(f[i][j],g[q.front()]-sum[i-1][j]);
			}
		}
		int ans=0;
		for(i=1;i<=m+1;i++)ans=max(ans,f[n+2][i]);
	    printf("%d\n",ans);
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值