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号
浙公网安备 33010602011771号