Food Delivery (区间dp)

本文探讨了一个外卖配送场景下的优化问题,目标是最小化顾客等待时间导致的不满程度总和。通过动态规划的方法,分析并给出了具体的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


题意:

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

分析:

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

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

我当时思考了很久,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}dp[i][j][0]=min{dp[i+1][j][0]+res_sum∗(xi+1−xi)∗V,dp[i+1][j][1]+res_sum∗(xj−xi)∗V}

dp[i][j][1]=min{dp[i][j1][0]+res_sum(xjxi)V,dp[i][j1][1]+res_sum(xjxj1)V}dp[i][j][1]=min{dp[i][j−1][0]+res_sum∗(xj−xi)∗V,dp[i][j−1][1]+res_sum∗(xj−xj−1)∗V}

res_sum其中,res_sum为未配送的客户的不满程度之和

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

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

yy完方程后,发现餐厅的坐标没有用过,以为一切都要重来了。
其实仔细想想,只有第1份配送的订单和餐厅的坐标位置相关,也就是当i==ji==j时,代价为sum[n]|xix|Vsum[n]∗|xi−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;
}
  • 代码又长又丑,请将就一下
### 食品配送中的区间动态规划算法 #### 定义问题 食品配送涉及多个地点之间的路径优化,这可以被建模成一个多阶段决策过程。在这个过程中,每个阶段代表从一个位置到另一个位置的选择。为了最小化总成本(如时间和距离),可以通过应用动态规划来解决问题。 #### 构造状态转移方程 假设存在一组餐厅和顾客的位置集合P={p_1, p_2,...,p_n}以及起始点S和终点E。定义dp[i][j]表示从i到j之间所有可能路线中最短的时间/费用开销。对于任意两个相邻节点pi 和pj (其中 i<j),如果直接连接这两点,则可以直接计算它们间的代价c(pi,pj); 否则就需要考虑经过其他中间站点k(i<k<j)的情况: \[ dp[i][j]=\min(dp[i][j],dp[i][k]+cost(k,j)) \] 这里`cost(k,j)`是从第k个位置到达第j个位置的成本函数[^1]。 #### 初始化边界条件 初始化时设置起点至各点的距离为已知值;而对于那些无法一步抵达的目标设为无穷大∞。具体来说就是当i=j时令dp[i][i]=0; 对于所有的i≠j且不存在直达边的情况下设定dp[i][j]=+∞。 #### 计算顺序 按照区间的长度从小到大地依次枚举每一对端点(i,j), 并更新对应的dp数组元素直到处理完最长的区间为止。这样做的目的是确保当我们尝试填充较大范围内的数据项之前,较小范围内已经被正确解决了[^3]。 ```python def interval_dp(n, cost_matrix): # Initialize DP table with infinity except diagonal elements which are set to zero. dp = [[float('inf')]*n for _ in range(n)] for i in range(n): dp[i][i] = 0 # Iterate over all possible intervals from smallest to largest. for length in range(2, n + 1): for start in range(n - length + 1): end = start + length - 1 for k in range(start, end): new_cost = dp[start][k] + cost_matrix[k][end] if new_cost < dp[start][end]: dp[start][end] = new_cost return dp[0][-1] ``` 此代码片段展示了如何实现上述提到的状态转移逻辑。它接受一个整数参数 `n`, 表示位置的数量,还有一个二维列表 `cost_matrix` 来描述各个位置之间的运输成本矩阵。最终返回的是从第一个位置到最后一个位置的最佳路径成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值