UVALive 3983 Robotruck

题目大意:有一个机器人要去送邮件,n个邮件,按照一定顺序来,机器人有一定的容量C,它可以顺序拿重量不超过C的邮件拿去送,然后,回来再拿再送,送完为止,注意,送的时也要按照拿的顺序送,给你每个邮件要送达的坐标,(0,0)为它拿邮件的地方,问你送完邮件机器人最少需要走的曼哈顿距离。

思路:很清楚,就是动态规划。d[ i ]表示送完i件,所需走过的最短距离。那么d[ i ] = min(d[ j ] + dis(j+1 ,0)+dis(j+1,i)+dis( i ,0),其中w[ j +1 ] + w[j+2] +……w[ i ] < =C) 。

因为C只有100,一开始我就写了个O(nC)的,先开始因为没看到送的时候也要按照顺序送、距离算成最短了而WA了两次,然后看到后,马上AC了。然后又看了书上写的,发现他的基本思想和我一样,但是他还做了优化,它维护了一个滑动区间,用s[ i ]表示从1到2,2到3……到i走过的距离,dis[ i ] 就只表示i到起始位置的距离,那么上述表达式可以写为d[ i ] = min ( d[ j ] + dis[ j+1 ] - sum[ j+1 ]) + dis[ i ] + sum[ i ] ,当然w和<=C。我们所要找的就是满足w条件的 d[ j ] + dis[ j+1 ] - sum[ j+1 ] 的最小值,他是一段区间,称为滑动区间。我们把这些区间里的元素看成一个队列,让它从小到大排列,每次删掉队头w不满足条件的元素,然后用队头直接更新d[ i ] ,删掉队尾比新加的元素对应的值还要大的元素,这样就维护了这个递增队列。由于每个元素最多进队列一次,总的复杂度为O(n)。

优化后的代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff ;

const int MAXN = 111111 ;

struct Pack
{
    int x,y;
    int w;
    Pack(){}
    Pack(int a,int b,int c) : x(a),y(b),w(c){}
} pack[MAXN];

int d[MAXN];

int sum[MAXN],tot_w[MAXN];

int cal_dis(int x)
{
    return (pack[x].x+pack[x].y);
}

int func(int x)
{
    return d[x]+cal_dis(x+1)-sum[x+1];
}

int q[MAXN];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int c,n;
        scanf("%d%d",&c,&n);
        int x1,y1,w1;
        sum[0]=0;
        pack[0] = Pack(0,0,0);
        tot_w[0] = 0;
        for(int i = 1;i<=n;i++)
        {
            scanf("%d%d%d",&x1,&y1,&w1);
            pack[i] = Pack(x1,y1,w1);
            sum[i] = sum[i-1] + abs(pack[i].x-pack[i-1].x)+abs(pack[i].y-pack[i-1].y);
            tot_w[i] = tot_w[i-1]+w1;
        }
        int front=0,rear=0;
        q[0] =0 ;
        for(int i = 1;i<=n;i++)
        {
            while(front<=rear&&tot_w[i]-tot_w[q[front]]>c) front++;
            d[i] = func(q[front])+cal_dis(i)+sum[i];
            while(front<=rear&&func(i)<=func(q[rear])) rear--;
            q[++rear] = i;
        }
        printf("%d\n",d[n]);
        if(T) puts("");
    }
    return 0;
}


下面是我自己写的没有优化的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff ;

const int MAXN = 111111 ;

struct Pack
{
    int x,y;
    int w;
    Pack(){}
    Pack(int a,int b,int c) : x(a),y(b),w(c){}
} pack[MAXN];

int d[MAXN];

int sum[MAXN];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int c,n;
        scanf("%d%d",&c,&n);
        int x1,y1,w1;
        sum[0]=0;
        pack[0] = Pack(0,0,0);
        for(int i = 1;i<=n;i++)
        {
            scanf("%d%d%d",&x1,&y1,&w1);
            pack[i] = Pack(x1,y1,w1);
            sum[i] = sum[i-1] + abs(pack[i].x-pack[i-1].x)+abs(pack[i].y-pack[i-1].y);
        }
        for(int i = 1;i<=n;i++)
            d[i] = INF;
        d[0]=0;
        for(int i = 0;i<n;i++)
        {
            int w = 0;
            for(int j = i+1;j<=n;j++)
            {
                w += pack[j].w;
                if(w>c) break;
                d[j] = min(d[j],d[i]+sum[j]-sum[i+1]+(pack[i+1].x+pack[i+1].y)+(pack[j].x+pack[j].y));
            }
        }
        printf("%d\n",d[n]);
        if(T) puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值