题意:F城由n+1个横向路和m+1个竖向路组成。你的任务是从最南边的路走到最北边的路,使得走过的路上的高兴值和最大(高兴值可能为负值)。同一段路不能经过两次,且不能从北往南走,在每条横向路所花时间不超过k。
思路:为了方便,从南往北走换成从北往南走,设d[ i ][ j ]为走到第( i , j )坐标时最大的高兴值,显然
d[ i ][ j ]=max(d[ i-1 ][ p ]+sum[ j ]-sum[ p ]),p 到 j 所花时间不超过 k,设f[ p ]=d[ i-1 ][ p ]-sum[ p ],那么怎么维护f[ p ]最大值呢,可用单调队列,每次更新一个 j 点,就把队列里f[ p ]值小于等于f[ j ]的值删除,然后把该点的值插入队列,注意的是该题要分从左到右和从右到左两个方向求d[ i ][ j ]的最大值。
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,k;
int suma[105][10005],sumc[105][10005];
int d[105][10005];
int q[10005];
void get()
{
for(int i=1;i<=n+1;i++)
{
suma[i][0]=0;
for(int j=1;j<=m;j++)
{
scanf("%d",&suma[i][j]);
suma[i][j]+=suma[i][j-1];
}
}
for(int i=1;i<=n+1;i++)
{
sumc[i][0]=0;
for(int j=1;j<=m;j++)
{
scanf("%d",&sumc[i][j]);
sumc[i][j]+=sumc[i][j-1];
}
}
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&k)&&n&&m)
{
int i,j,p;
get();
int front,rear;
for(i=1;i<=n+1;i++)
{
front=rear=0;
q[0]=0;
for(j=0;j<=m;j++)
{
while(front<=rear&&sumc[i][j]-sumc[i][q[front]]>k)
front++;
d[i][j]=d[i-1][j];
if(front<=rear)
d[i][j]=max(d[i][j],d[i-1][q[front]]+suma[i][j]-suma[i][q[front]]);
while(front<=rear&&d[i-1][j]>=d[i-1][q[rear]]+suma[i][j]-suma[i][q[rear]])
rear--;
q[++rear]=j;
}
rear=front=0;
q[0]=m;
for(j=m;j>=0;j--)
{
while(front<=rear&&sumc[i][q[front]]-sumc[i][j]>k)
front++;
if(front<=rear)
d[i][j]=max(d[i][j],d[i-1][q[front]]+suma[i][q[front]]-suma[i][j]);
while(front<=rear&&d[i-1][j]>=d[i-1][q[rear]]+suma[i][q[rear]]-suma[i][j])
rear--;
q[++rear]=j;
}
}
int ans=0;
for(i=0;i<=m;i++)
ans=max(ans,d[n+1][i]);
printf("%d\n",ans);
}
}