[区间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;
}