Food Delivery (区间dp)

Description

When we are focusing on solving problems, we usually prefer to stay in front of computers rather than go out for lunch. At this time, we may call for food delivery.

Suppose there are N people living in a straight street that is just lies on an X-coordinate axis. The ith person’s coordinate is Xi meters. And in the street there is a take-out restaurant which has coordinates X meters. One day at lunchtime, each person takes an order from the restaurant at the same time. As a worker in the restaurant, you need to start from the restaurant, send food to the N people, and then come back to the restaurant. Your speed is V-1 meters per minute.

You know that the N people have different personal characters; therefore they have different feeling on the time their food arrives. Their feelings are measured by Displeasure Index. At the beginning, the Displeasure Index for each person is 0. When waiting for the food, the ith person will gain Bi Displeasure Index per minute.

If one’s Displeasure Index goes too high, he will not buy your food any more. So you need to keep the sum of all people’s Displeasure Index as low as possible in order to maximize your income. Your task is to find the minimal sum of Displeasure Index.

Input

The input contains multiple test cases, separated with a blank line. Each case is started with three integers N ( 1 <= N <= 1000 ), V ( V > 0), X ( X >= 0 ), then N lines followed. Each line contains two integers Xi ( Xi >= 0 ), Bi ( Bi >= 0), which are described above.

You can safely assume that all numbers in the input and output will be less than 231 - 1.

Please process to the end-of-file.

Output

For each test case please output a single number, which is the minimal sum of Displeasure Index. One test case per line.

Sample Input

5 1 0
1 1
2 2
3 3
4 4
5 5

Sample Output

55


题意:

N N 个人住在同一条街,可视为X坐标轴,第 i i 个人位于xi位置,餐厅位于 x x 位置。现在餐厅员工需要给这N个人派送外卖,速度为 V1 V − 1 米/分钟。一开始,每个人的不满程度都为0,每等待一分钟,第 i i 个人不满程度会增加Bi。当不满程度达到一定值的时候,他不再会下单,所以要尽可能使所有人的不满程度之和最小,问最小的不满程度之和是多少?

分析:

首先,将 N N 个人的坐标按x大小排序,重新编号

假定目前配送完 [i,j] [ i , j ] ,那么可以停留在 xi x i 位置或 xj x j 位置。为什么不会停留在任意位置呢?假设最终停留在 xk x k 位置, xixkxj x i ≤ x k ≤ x j ,为了配送其它订单,必然要向 xi x i xj x j 移动,那么就没有必要先去配送 xi x i xj x j 的订单,那样更浪费时间。
至此,我们可以得到转移状态 dp[i][j][stay] d p [ i ] [ j ] [ s t a y ] ,当 stay=0 s t a y = 0 时,表示停留在 xi x i 位置,当 stay=1 s t a y = 1 时,表示停留在 xj x j 位置。

我当时思考了很久,yy出了转移方程

dp[i][j][0]=min{dp[i+1][j][0]+res_sum(xi+1xi)V,dp[i+1][j][1]+res_sum(xjxi)V} d p [ i ] [ j ] [ 0 ] = m i n { d p [ i + 1 ] [ j ] [ 0 ] + r e s _ s u m ∗ ( x i + 1 − x i ) ∗ V , d p [ i + 1 ] [ j ] [ 1 ] + r e s _ s u m ∗ ( x j − x i ) ∗ V }

dp[i][j][1]=min{dp[i][j1][0]+res_sum(xjxi)V,dp[i][j1][1]+res_sum(xjxj1)V} d p [ i ] [ j ] [ 1 ] = m i n { d p [ i ] [ j − 1 ] [ 0 ] + r e s _ s u m ∗ ( x j − x i ) ∗ V , d p [ i ] [ j − 1 ] [ 1 ] + r e s _ s u m ∗ ( x j − x j − 1 ) ∗ V }

res_sum 其 中 , r e s _ s u m 为 未 配 送 的 客 户 的 不 满 程 度 之 和

后来验证了转移方程的正确性,以 dp[i][j][0] d p [ i ] [ j ] [ 0 ] 的转移方程为例:

dp[i][j][0] d p [ i ] [ j ] [ 0 ] 表示的是配送完 [i,j] [ i , j ] 的订单,并且最终停留在 xi x i 的位置,也就是说, i i 订单是在这些订单中最后配送的,所以是由dp[i+1][j][0/1]转移而来。如果上一次停留在 xi+1 x i + 1 位置,那么这段路程需要的时间是 (xi+1xi)V ( x i + 1 − x i ) ∗ V ,如果上一次停留在 xj x j 位置,那么这段路程需要的时间是 (xjxi)V ( x j − x i ) ∗ V
而这部分等待时间,除了已经配送的第 i+1 i + 1 j j 个订单,其它人都需要等待,所以需要等待的客户的不满程度之和是sum[n](sum[j]sum[i])。将两个式子相乘就是此次配送会造成的代价。
同理可证, dp[i][j][1] d p [ i ] [ j ] [ 1 ] 的转移方程的正确性。

yy完方程后,发现餐厅的坐标没有用过,以为一切都要重来了。
其实仔细想想,只有第1份配送的订单和餐厅的坐标位置相关,也就是当 i==j i == j 时,代价为 sum[n]|xix|V s u m [ n ] ∗ | x i − x | ∗ V


代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<time.h>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>

using namespace std;

#define bll long long

const int maxn = 1100;
struct node
{
    int x,v;
    node (int n1 = 0, int n2 = 0)
    {
        x = n1, v = n2;
    }
    friend bool operator < (const node n1, const node n2)
    {
        return n1.x<n2.x;
    }
}a[maxn];
int n,V,shop;
bll sum[maxn],dp[maxn][maxn][2];

bll calc(int r,int l,int u,int v)
{
    bll tmp = sum[n]-sum[r]+sum[l];
    return tmp*(a[u].x-a[v].x)*V;
}

bll solve(int i,int j,int stay)
{
    if (dp[i][j][stay] != -1) return dp[i][j][stay];
    if (i == j)
    {
        dp[i][j][stay] = sum[n]*(abs(a[i].x-shop))*V;
        return dp[i][j][stay];
    }
    if (stay == 0)
    {
        dp[i][j][0] = min(solve(i+1,j,0)+calc(j,i,i+1,i),solve(i+1,j,1)+calc(j,i,j,i));
    }
    else
    {
        dp[i][j][1] = min(solve(i,j-1,0)+calc(j-1,i-1,j,i),solve(i,j-1,1)+calc(j-1,i-1,j,j-1));
    }
    return dp[i][j][stay];
}

int main()
{
    while (scanf("%d %d %d",&n,&V,&shop)!=EOF)
    {
        for (int i=1;i<=n;i++) scanf("%d %d",&a[i].x,&a[i].v);
        sort(a+1,a+n+1);
        sum[0] = 0;
        for (int i=1;i<=n;i++) sum[i] = sum[i-1]+a[i].v;
        for (int i=1;i<=n;i++)
            for (int j=i;j<=n;j++) 
                dp[i][j][0] = dp[i][j][1] = -1;
        long long ans = min(solve(1,n,0),solve(1,n,1));
        printf("%lld\n",ans);
    }
    return 0;
}
  • 代码又长又丑,请将就一下
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值