测试地址:One hundred layer
题目大意:一座塔有
N
层,每一层有一排
做法:本题是一个单调队列优化DP的题目。
这一题是DP应该能很显然地看出来,设
f(i,j)
为从第
i
层第
f(i,j)=max(max{f(i+1,k)+w(i,k,j)|j−T≤k≤j},max{f(i+1,k)+w(i,j,k)|j≤k≤j+T})
其中
w(i,j,k)
为第
i
行第
w
可以用前缀和
考虑到向左走和向右走两种情况是对称的,所以我们这里只讨论向左走的情况。我们处理出了前缀和
sum(i,j)=∑jk=1aik
,那么上面的式子可以写成:
max{f(i+1,k)+sum(i,j)−sum(i,k−1)|j−T≤k≤j}
我们发现
sum(i,j)
在求
f(i,j)
时可以算作常量,可以提出括号,所以我们就是要求:
max{f(i+1,k)−sum(i,k−1)|j−T≤k≤j}
观察发现,
f(i+1,k)−sum(i,k−1)
是由变量
k
所唯一确定的常量,而
对于向右走的情况同理,这里就不赘述了,最后算法的时间复杂度为
O(NM)
,完美解决了这一题。
最后还需要注意的是,输入可能有多组测试数据,小心不要被坑了……
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 1000000000
using namespace std;
int n,m,x,t;
int a[110][10010],f[110][10010],lsum[110][10010],rsum[110][10010];
int q[10010],head,tail;
int main()
{
while(scanf("%d%d%d%d",&n,&m,&x,&t)!=EOF)
{
memset(lsum,0,sizeof(lsum));
memset(rsum,0,sizeof(rsum));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=m;i++) f[n+1][i]=0;
for(int i=1;i<=n;i++)
{
lsum[i][0]=rsum[i][m+1]=0;
for(int j=1;j<=m;j++) lsum[i][j]=lsum[i][j-1]+a[i][j];
for(int j=m;j>=1;j--) rsum[i][j]=rsum[i][j+1]+a[i][j];
}
for(int i=n;i>=1;i--)
{
head=1,tail=0;
for(int j=1;j<=m;j++)
{
f[i][j]=-inf;
while(head<=tail&&q[head]<j-t) head++;
while(head<=tail&&f[i+1][j]-lsum[i][j-1]>=f[i+1][q[tail]]-lsum[i][q[tail]-1]) tail--;
q[++tail]=j;
f[i][j]=max(f[i][j],f[i+1][q[head]]+lsum[i][j]-lsum[i][q[head]-1]);
}
head=1,tail=0;
for(int j=m;j>=1;j--)
{
while(head<=tail&&q[head]>j+t) head++;
while(head<=tail&&f[i+1][j]-rsum[i][j+1]>=f[i+1][q[tail]]-rsum[i][q[tail]+1]) tail--;
q[++tail]=j;
f[i][j]=max(f[i][j],f[i+1][q[head]]+rsum[i][j]-rsum[i][q[head]+1]);
}
}
printf("%d\n",f[1][x]);
}
return 0;
}