zoj 3469 Food Delivery
题面
一维数轴上有一个外卖配送员和 (1000) 个客户,每个客户 (i) 的怒气值是他收到外卖的时间乘 (b[i]) ( (b[i]) 由题目给定),给出配送员速度,求一个方案使得所有客户的怒气值之和最小,输出最小怒气值之和。
题解
收到货的客户一定处在连续的一段区间内,做法是区间dp。
我们用 (v_i) 表示第 (i) 个收到外卖的客户的 (b) 值,(x_i) 表示第 (i) 个收到外卖的客户的坐标。 (O) 表示配送员初始坐标。
(总的怒气值=)
(v_1*(|x_1-O|) +)
(v_2*(|x_1-O|+|x_2-x_1|) +)
(v_3*(|x_1-O|+|x_2-x_1|+|x_3-x_2|) +)
(... +)
(v_n*(|x_1-O|+|x_2-x_1|+|x_3-x_2|+...+|x_n-x_{n-1}|))
(=)
(|x_1-O|*(v_1+v_2+...+v_n) +)
(|x_2-x_1|*(v_2+...+v_n) +)
(... +)
(|x_n-x_{n-1}|*(v_n))
(f[i][j][0])表示(i)到(j)这段区间的客户都已经收到外卖,此时外卖员在(i)处,假设这段区间有(t)个人。(f[i][j][0])为上式中前(t)行。
(f[i][j][1])表示外卖员在(j)处,其他与(f[i][j][0])同理。
代码
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define sz(x) (int)x.size()
#define de(x) cout<< #x<<" = "<<x<<endl
#define dd(x) cout<< #x<<" = "<<x<<" "
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
//------
const int N=1010;
const ll inf=(1ll<<31);
int n,v,x;
ll f[N][N][2], sum[N];
pii a[N];
ll Sum(int l,int r) {
ll ans=sum[n+1]-(sum[r+1]-sum[l]);
return ans;
}
int main() {
while(~scanf("%d%d%d",&n,&v,&x)) {
///init
rep(i,0,n+1) rep(j,0,n+1) rep(k,0,2) f[i][j][k]=inf;
///read
rep(i,1,n+1) {
int x,y;scanf("%d%d",&x,&y);
a[i]=mp(x,y);
}
///solve
a[0]=mp(x,-1);
sort(a,a+n+1);
rep(i,0,n+1) sum[i+1]=sum[i]+max(a[i].se, 0);
rep(i,0,n+1) if(a[i].se==-1) f[i][i][0]=f[i][i][1]=0;
rep(len,1,n+1) {
for(int i=0;i+len<=n;++i) {
int j=i+len;
ll t0=1ll*(a[i+1].fi-a[i].fi)*Sum(i+1, j);
ll t1=1ll*(a[j].fi-a[i].fi)*Sum(i+1, j);
f[i][j][0]=min(f[i+1][j][0]+t0,f[i+1][j][1]+t1);
ll t2=1ll*(a[j].fi-a[i].fi)*Sum(i, j-1);
ll t3=1ll*(a[j].fi-a[j-1].fi)*Sum(i, j-1);
f[i][j][1]=min(f[i][j-1][0]+t2,f[i][j-1][1]+t3);
}
}
printf("%lld
",min(f[0][n][0], f[0][n][1])*v);
}
return 0;
}