LA 4327: Parade
题目来源:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13893
分析:
首先不难想到枚举“当前点是从上一行哪里来的”,设F[i][j]为走到第i行,第j列的点时的最大开心值,因此有DP方程:
F[i][j]=max { max{F[i][l]+S[i][j]-S[i][l] | L[i][j]<=l<=j} ,max{F[i][r]+S[i][r]-S[i][j] | j<=r<=R[j]} }
其中,S[i]为第i行的点开心值前缀和,L[i][j],R[i][j]分别为此点往左/右走的最大距离,这些值都可以在O(nm)时间内递推出来
但是这样复杂度为O(nm^2),太高了,需要优化。
由于距离为正数,因此在同一行(从左往右),决策涉及的区间一定是向右不断平移的,再将DP方程变形为:
F[i][j]-S[i][j]=max
可以用单调队列解决,复杂度为O(nm)
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
const int maxn=100+23,maxm=10000+23;
struct Node{
int num,add;
} Q[maxm];
int ans,sum,st,en,n,m,k,d_max;
int dist[maxn][maxm],s[maxn][maxm],L[maxn][maxm],R[maxn][maxm],LD[maxm],RD[maxm],F[maxn][maxm];
int readint()
{
char c=getchar();
while (!isdigit(c) && (c!='-')) c=getchar();
int mark;
if (c=='-') {mark=-1;c=getchar();} else mark=1;
int x=0;
while (isdigit(c))
{
x=x*10+(c-'0');
c=getchar();
}
return x*mark;
}
void Push(int num,int add) //单调队列插入函数
{
while ((st<=en) && (Q[en].num<num)) en--;
Node k; k.num=num;k.add=add;
Q[++en]=k;
}
void init()
{
n++;m++;
rep(i,1,n)
{
s[i][1]=0;
rep(j,2,m)
{
k=readint();
s[i][j]=s[i][j-1]+k; //计算开心值前缀和
}
}
rep(i,1,n) rep(j,1,m-1) dist[i][j]=readint();
memset(L,0,sizeof(L));
rep(i,1,n)
{
st=1,sum=0;
L[i][1]=1;
rep(j,2,m)
{
sum+=dist[i][j-1];
while ((sum>d_max) && (st<j)) sum-=dist[i][st++];
L[i][j]=st; //计算最左点
}
}
rep(i,1,n)
{
st=m,sum=0;
R[i][m]=m;
dep(j,m-1,1)
{
sum+=dist[i][j];
while ((sum>d_max) && (st>j)) sum-=dist[i][--st];
R[i][j]=st; //计算最右点
}
}
}
int main()
{
while ((scanf("%d%d%d",&n,&m,&d_max)==3) && n && m && d_max)
{
init();
rep(i,0,m) F[0][i]=0;
rep(i,1,n)
{
st=1,en=0;
memset(LD,0,sizeof(LD));
LD[1]=F[i-1][1];
Push(LD[1]-s[i][1],1);
rep(j,2,m)
{
Push(F[i-1][j]-s[i][j],j);
while ((st<en) && (Q[st].add<L[i][j])) st++;
LD[j]=Q[st].num+s[i][j];
}
st=1,en=0;
memset(RD,0,sizeof(RD));
RD[m]=F[i-1][m];
Push(RD[m]+s[i][m],m);
dep(j,m-1,1)
{
Push(F[i-1][j]+s[i][j],j);
while ((st<en) && (Q[st].add>R[i][j])) st++;
RD[j]=Q[st].num-s[i][j];
}
rep(j,1,m)
F[i][j]=max(LD[j],RD[j]);
}
ans=0;
rep(i,1,m) ans=max(ans,F[n][i]);
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号