UVALive 4327 Parade

题目大意:给你一个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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值