题目大意:给你一个n*m个窗格,n+1条边横边,m+1条竖边,每段横边都有一个val值和len,现在要求从最底下那条横边任意选一个节点开始沿着边走,走到最上面那条横边的任何一个节点,不能走走过的路,竖边只能往上走,不能往下走,每行横着走的len之和不能大于k,问你经过的路径val之和最大是多少?
思路:模型是很简单的,就是设d[ i ][ j ] 表示到 i行j个节点的最大val值,则d[ i ][ j ] = max(d[ i - 1][ k ] + val(k , j )ORval(j , k ),其中len( k , j),len( j ,k ) <=k )。但是这样计算的时间复杂度为O(n*(m^2)),会爆掉,需要优化。d[ i - 1][ k ] + val(k , j ) = d[ k - 1 ][ k ] + val_sum[ k ] - val_sum[ j ],k<=j,那么我们可以用优先队列来维护,里面按照d[ k - 1 ][ k ] + val_sum[ k ] 作为权值从大到小进行排序,k>=j那边也一样,两边都扫一遍,每次取的时间复杂度都是O(1),这样总的时间复杂度就会是O(n*m)。这里还要注意,优先队列操作时要先进行rear--,加进去这一步,再进行head++,取合法的head,因为k可以等于j。
好题,表示用优先队列进行优化用的不是很熟,先开始也没想到,mark一下,优先队列维护,好东西!
由于val值和len值都是在边上的,而dp是在节点上的,一会儿-1,一会儿不-1的,代码有点乱,关键在于思想吧。。 = =
代码如下:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define MP make_pair
typedef long long lld;
const int INF = 0x0fffffff ;
struct Road
{
int val,len;
} road[111][11111] ;
int sum_val[111][11111],sum_len[111][11111];
int d[111][11111];
int q[11111];
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k)&&(n+m+k))
{
n++;
for(int i = 1;i<=n;i++)
{
sum_val[i][0] = 0;
for(int j = 1;j<=m;j++)
{
scanf("%d",&road[i][j].val);
sum_val[i][j] = sum_val[i][j-1] + road[i][j].val;
}
}
for(int i = 1;i<=n;i++)
{
sum_len[i][0] = 0;
for(int j = 1;j<=m;j++)
{
scanf("%d",&road[i][j].len);
sum_len[i][j] = sum_len[i][j-1] + road[i][j].len;
}
}
for(int i = 1;i<=(m+1);i++)
d[0][i] = 0;
for(int i = 1;i<=n;i++)
{
int front = 0,rear = 0;
q[0] = 1;
for(int j = 1;j<=(m+1);j++)
{
while(front<=rear&& d[i-1][q[rear]] - sum_val[i][q[rear]-1] <= d[i-1][j] - sum_val[i][j-1] ) rear--;
q[++rear] = j;
while(front<=rear&& sum_len[i][j-1] - sum_len[i][q[front]-1] > k) front++;
d[i][j] = d[i-1][q[front]] + sum_val[i][j-1] - sum_val[i][q[front]-1];
}
front = 0,rear = 0;
q[0] = m+1;
for(int j = (m+1);j>=1;j--)
{
while(front<=rear&& d[i-1][q[rear]] + sum_val[i][q[rear]-1] <= d[i-1][j] + sum_val[i][j-1]) rear--;
q[++rear] = j;
while(front<=rear&& sum_len[i][q[front]-1] - sum_len[i][j-1] > k) front++;
d[i][j] = max(d[i][j],d[i-1][q[front]] + sum_val[i][q[front]-1] - sum_val[i][j-1]);
}
/*for(int j = 1;j<=(m+1);j++)
printf("d[%d][%d] = %d\n",i,j,d[i][j]);*/
}
int ans = -INF;
for(int i = 1;i<=(m+1);i++)
ans = max(ans,d[n][i]);
printf("%d\n",ans);
}
return 0;
}