[区间DP 好题] Food Delivery ZOJ - 3469

           [区间DP 好题]   Food Delivery ZOJ - 3469 

题目大意:一家外卖店送外卖,同时有多个买家叫了外卖,假设他们都在一条直线上。每个买家都有一个焦虑值,等的时间越长,焦虑值越大。现告诉你外卖员的位置,移动速度,以及各个买家在直线上的坐标以及他们在单位时间内的愤怒值,现在外卖员想所有买家愤怒的总和尽可能的小,问你最小能是多少。(假设外卖员把外卖送到买家手里的时间忽略不计)。
分析:这道题看到以后是一点思路都没有,一开始还感觉可以贪心做。。。后来看了大佬的博客,才知道是区间dp。简单说下思路,具体的分析在代码里。一开始的时候,外卖员不是往左去就是往右去,所以我们可以从外卖店开始,左右扩散。 dp[i][j]表示送完该区间内的所有买家最低的愤怒值,还有一个问题,就是送完区间[i,j]以后,外卖员是停在了左侧还是右侧,这将影响我们接下来的决策,因此状态可以用dp[i][j][2]表示。
AC代码:

#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
#define MAX 1100000000
#define MIN -1
#define ll long long
using namespace std;
struct node
{
    int x,b;
};
bool cmp(node a,node b)
{
    return a.x<b.x;
}
int dp[1005][1005][2];
int main()
{
    int n,v,x;
    while(~scanf("%d %d %d",&n,&v,&x))
    {
        node data[1005];
        int re;
        for(int i=1;i<=n;i++)
           scanf("%d %d",&data[i].x,&data[i].b);
        int num[1005];
        n++;
        data[n].x=x;
        data[n].b=0;
        sort(data+1,data+n+1,cmp);
        num[0]=0;
        num[1]=data[1].b;
        for(int i=2;i<=n;i++)
            num[i]=num[i-1]+data[i].b;
        for(int i=1;i<=n;i++)
        {
            if(data[i].x==x)
                re=i;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
        {
            dp[i][j][0]=MAX;
            dp[i][j][1]=MAX;
        }
        dp[re][re][0]=0;
        dp[re][re][1]=0;
        /*关于后效性
        每选择一个买家去送,对之前的状态是没有影响的,因为后面的时间不能影响之前的时间
        关于状态定义
        op[i][j]表示区间i到区间j最低的生气值
        但此时有两种情况,即送完区间i,j时,是停在在左侧还是右侧,
        因此需要用dp[i][j][2];0表示左侧,1表示右侧;
        关于状态转移
          dp[i][j][0]可以从dp[i+1][j][0]以及dp[i+1][j][1]推算出来
          dp[i][j][1]可以从dp[i][j-1][0]以及dp[i][j-1][1]推算出来
          以dp[i+1][j][1]转移到dp[i][j][0]为例
            即从j移动到i,移动的时间t为(data[j].x-data[i].x)/v
            由于每次移动,都会影响后面人取餐的时间,因此在当前的决策中,必须考虑对后面人的影响
            假设单位时间内后面人的生气的总值为key,那么总的影响就是key*t
           (一开始我有这样的疑惑:后面每个人取餐的时间不一致,为什么在这里考虑影响的时候可以通过
            key*t实现,为什么把每个人的生气值直接*t,仔细一想,在每一次决策时,其实对后序所
            有买家的影响只有一个变量t)
        关于最优子结构
        对于区间[i,j],该区间是通过子区间的最优解推出来的,所有必然满足最优子结构
        一些细节处理
            1. 将外卖店的坐标加入data数组,生气值设为0,从该点向两边递推
            2. 将买家的生气值做前缀数组处理,因为每次转移时,我们都要知道后面买家总生气值
            3. 讲外卖店在dp中的值初始化为0*/
        for(int i=re;i>=1;i--)
            for(int j=re;j<=n;j++)
        {

            if(i==j)
                continue;
            else
            {
               int key=num[i-1]+num[n]-num[j];
               dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][0]+(key+data[i].b)*(data[i+1].x-data[i].x));
               dp[i][j][0]=min(dp[i][j][0],dp[i+1][j][1]+(key+data[i].b)*(data[j].x-data[i].x));
               dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][0]+(key+data[j].b)*(data[j].x-data[i].x));
               dp[i][j][1]=min(dp[i][j][1],dp[i][j-1][1]+(key+data[j].b)*(data[j].x-data[j-1].x));
            }
        }
        printf("%d\n",min(dp[1][n][0],dp[1][n][1])*v);

    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值