zoj3469 Food Delivery---区间dp

题目链接:https://vjudge.net/problem/ZOJ-3469

简单题意(来源于网络,懒得写了):在x轴上有n个客人,每个客人每分钟增加的愤怒值不同。给出客人和餐厅的位置,以及客人每分钟增加的愤怒值,和送餐行走一公里需要的时间,问送完n个客人的外卖最小愤怒值。

f[i][j][0/1]表示i,j之间全部送完的最小值,最后停在i/j的最小值。那么有转移方程(这儿s[i]表示前i个人不开心值的和

f[i][j][0]=min(f[i][j][0],f[i+1][j][0]+(a[i+1].x-a[i].x)*(s[n+1]-s[j]+s[i]));

f[i][j][0]=min(f[i][j][0],f[i+1][j][1]+(a[j].x-a[i].x)*(s[n+1]-s[j]+s[i]));

f[i][j][1]=min(f[i][j][1],f[i][j-1][0]+(a[j].x-a[i].x)*(s[n+1]-s[j-1]+s[i-1]));

f[i][j][1]=min(f[i][j][1],f[i][j-1][1]+(a[j].x-a[j-1].x)*(s[n+1]-s[j-1]+s[i-1]))。

这题的状态设计和区间扩展的方法都值得思考。以前写区间dp,状态一直是:f[i][j]表示从i到j...或者f[i][j][k]表示i到j的数分割成k份...这道题由于送完餐快递员一定在两个端点之一(否则相当于中间有一个没送,还要折回来送,不是最优解),而且要考虑当前送完餐的区间,是由哪个点走过来的,所以设计状态f[i][j][0/1]。转移方程看起来有点麻烦,但其实不难写,而且看到1000的数据范围,也能知道是小区间沿着边界往外扩张,也就是考虑f[i+1][j][0/1],f[i][j-1][0/1]。但是和之前的题目不同,这是从餐馆位置p往两侧扩张的,这一点体现在程序打*的注释中

#include<bits/stdc++.h>
using namespace std;

const int N=1000+10;
struct st{ int b,x;}a[N];
int f[N][N][2],s[N],n,v,x,p,i,j,k;

bool cmp(st p1,st p2){return p1.x<p2.x;}

int main(){
	while (~scanf("%d%d%d",&n,&v,&x)){
	  for (i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].b);
	  a[n+1].x=x; a[n+1].b=0;
	  sort(a+1,a+n+2,cmp);
	  for (i=1;i<=n+1;i++) s[i]=s[i-1]+a[i].b;
	  for (i=1;i<=n+1;i++)
	    if (a[i].x==x){p=i; break;}
      memset(f,0x3f,sizeof(f));
	  f[p][p][0]=f[p][p][1]=0;
	  for (i=p;i>=1;i--)   //*
	    for (j=p;j<=n+1;j++){    //*
	      f[i][j][0]=min(f[i][j][0],f[i+1][j][0]+(a[i+1].x-a[i].x)*(s[n+1]-s[j]+s[i]));
	      f[i][j][0]=min(f[i][j][0],f[i+1][j][1]+(a[j].x-a[i].x)*(s[n+1]-s[j]+s[i]));
	      f[i][j][1]=min(f[i][j][1],f[i][j-1][0]+(a[j].x-a[i].x)*(s[n+1]-s[j-1]+s[i-1]));
	      f[i][j][1]=min(f[i][j][1],f[i][j-1][1]+(a[j].x-a[j-1].x)*(s[n+1]-s[j-1]+s[i-1]));
		}
	  printf("%d\n",min(f[1][n+1][0],f[1][n+1][1])*v);
	}
	return 0;
}

  

posted @ 2020-09-01 22:52  coastal_taipan  阅读(135)  评论(0编辑  收藏  举报