Food Delivery ZOJ-3469 (区间dp)

传送门

(补一道欠了很久的题)

题意:在横坐标轴上给出了n个人的坐标,从x0处出发送餐,每过1分钟第i个人的不满意度会增加v[i],求送完所有人的餐后,不满意度之和的最小值。给出了送餐员的速度v
(1<=n<=1000)

分析:一道很经典的区间dp题。考虑在第[1,n]个人之间进行求解。

我们接下来考虑当求出了完成[i,j]这些人对总体不满意度的贡献之后,如何把答案向两边扩展?

  • [i,j]->[i-1,j],要向左扩展一位,送餐员此时只有停留在i / j两种情况,我们分别转移并取最小值 。向右扩展同理。
  • 于是状态可以设为:dp[i][j][0/1]表示处理完[i,j]区间之后,0表示停在i位置 1表示停在j位置的全局最小贡献。dp[i][i][0]和dp[i][i][1]相等, 作为边界。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define ri register 
using namespace std;
const ll INF=0x3f3f3f3f3f3f3f;
const int maxn = 1e3+10;

//我们从左往右排好序 dp[i][j][0/1]表示送完[i,j]这段 停在左/右边的时候 对全局贡献最小的不满意度
int n,v,x0;
struct node
{
    int x,b;
    bool operator < (const node & f) const 
    {
        return x<f.x;
    }
}a[maxn];
ll sum[maxn];//[1,i]这些人 每分钟增加的不满意度之和
ll dp[maxn][maxn][2];//送完[i,j]这段 停在左/右边的时候 对全局贡献最小的不满意度
//如果把状态设为 [i,j]这段的不满意度最小值的话  因为时间这里有后效性了 无法转移
//设成全局贡献 由于[i,j]的全局贡献已经求出来了 所以向两边转移的时候不需要再考虑
//只需要考虑向两边转移的过程中 对全局的贡献

int main()
{
    while(~scanf("%d %d %d",&n,&v,&x0))
    {
        memset(dp,INF,sizeof dp);
        for(ri int i=1;i<=n;i++) scanf("%d %d",&a[i].x,&a[i].b);
        sort(a+1,a+n+1);
        sum[0]=0;
        for(ri int i=1;i<=n;i++) 
        {
            sum[i]=sum[i-1]+a[i].b;
        }
        for(int i=1;i<=n;i++) 
        {
            //所有人的不满意度*到达第i个人所需的时间
            dp[i][i][0]=dp[i][i][1]=sum[n]*abs(x0-a[i].x);
        }
        for(int r=2;r<=n;r++) 
        {
            for(int i=1;i+r-1<=n;i++) 
            {
                int j=i+r-1;
                //[i+1,j]区间向左边扩展1格得到[i,j] 路程*不满意度之和 
                dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][0]+1ll*abs(a[i+1].x-a[i].x)*(sum[n]-sum[j]+sum[i]));
                dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][1]+1ll*abs(a[j].x-a[i].x)*(sum[n]-sum[j]+sum[i]));

                dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+1ll*abs(a[i].x-a[j].x)*(sum[n]-sum[j-1]+sum[i-1]));
                dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+1ll*abs(a[j-1].x-a[j].x)*(sum[n]-sum[j-1]+sum[i-1]));
            }
        }
        //最终答案再除以速度1/v 得到时间
        cout<<min(dp[1][n][0],dp[1][n][1])*v<<'\n';
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值