poj3926 parade (单调队列+dp)
题意:有n行路,每行路被分成m段,每一段有长度和权值,要求从最下面一行走到最上面一行某个位置,可以从相邻两行的同一列交点往上走,并且在同一行走的长度要<=K,求走过的最大权值
设f[i][j]为到第i行,第j个交点的最大值
设sumvalue[i][j,k]为第i行从第j个交点到第k个交点经过道路的权值之和
设sumtime[i][j,k]为第i行从第j个交点到第k个交点经过道路的长度之和
则f[i][j]=max{
f[i-1][k]+sumvalue[i][k,j] ,k<=j且sumtime[i][k,j]<=K
f[i-1][k]+sumvalue[i][j,k] ,k>j且sumtime[i][j,k]<=K
}
以第一个方程为例,单调队列记某行i∈[k,j]的f[][i]+sumvalue[][i..j]的最大值即可
其中,在j++的时候,由于要给所有数加上val[][j]的值,我们就假装大家一起
减掉了val[][j]的值,只给要新进的f[][j]+val[][j]减掉sumvalue[][0..j]即可
(最后给f值的时候别忘了加回来)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define LL long long int 5 using namespace std; 6 const int maxn=110,maxm=10010; 7 8 int rd(){ 9 int x=0,neg=1;char c=getchar(); 10 while(c<'0'||c>'9') {if(c=='-') neg=-1;c=getchar();} 11 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 12 return x*neg; 13 } 14 15 int N,M,K; 16 int val[maxm][maxn],len[maxm][maxn],head,tail; 17 LL f[maxm][maxn],q[maxm][2],ans; 18 19 inline void insert(LL x,int y){ 20 for(int i=tail;i>=head;i--){ 21 if(q[i][0]>x){ 22 tail=i+1;q[tail][0]=x;q[tail][1]=y; 23 return; 24 } 25 }tail=head;q[tail][0]=x;q[tail][1]=y; 26 } 27 28 int main(){ 29 int i,j,k; 30 //freopen("3926.in","r",stdin); 31 while(1){ 32 N=rd(),M=rd(),K=rd();if(!N) break; 33 for(i=1;i<=N+1;i++){ 34 for(j=1;j<=M;j++) val[j][i]=rd(); 35 }for(i=1;i<=N+1;i++){ 36 for(j=1;j<=M;j++) len[j][i]=rd(); 37 } 38 memset(f,0,sizeof(f));ans=0; 39 for(i=1;i<=N+1;i++){ 40 LL sv=0,st=0; 41 tail=head=1;memset(q,0,sizeof(q)); 42 for(j=0;j<=M;j++){ 43 sv+=val[j][i],st+=len[j][i]; 44 insert(f[j][i-1]-sv,st); 45 while(st-q[head][1]>K) head++; 46 f[j][i]=q[head][0]+sv; 47 } 48 sv=0;st=0; 49 tail=head=1;memset(q,0,sizeof(q)); 50 for(j=M;j>=0;j--){ 51 sv+=val[j+1][i],st+=len[j+1][i]; 52 insert(f[j][i-1]-sv,st); 53 while(st-q[head][1]>K) head++; 54 f[j][i]=max(f[j][i],q[head][0]+sv); 55 } 56 } 57 for(j=0;j<=M;j++) ans=max(ans,f[j][N+1]); 58 printf("%d\n",ans); 59 } 60 61 }