[区间DP 好题] Food Delivery ZOJ

来源:互联网 发布:网络运维管理系统 编辑:程序博客网 时间:2024/06/08 06:15
           [区间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 longusing 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;}
原创粉丝点击