题目链接: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 ",min(f[1][n+1][0],f[1][n+1][1])*v); } return 0; }