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\n",min(f[0][n][0], f[0][n][1])*v);
}
return 0;
}