思路:把小于x和大于x存成两个集合,然后dp[i][j][v]表示左边送完i个,右边送完j个,现在位置在左/右的代价,然后进行转移即可,具体转移见代码
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1005;
const ll INF = 0x3f3f3f3f3f3f3f;
int n, v, x, ln, rn;
ll dp[N][N][2];
struct SB {
int x, b;
SB() {}
SB(int x, int b) {
this->x = x;
this->b = b;
}
} l[N], r[N];
int suml[N], sumr[N];
bool cmp1(SB a, SB b) {
return a.x < b.x;
}
bool cmp2(SB a, SB b) {
return a.x > b.x;
}
int main() {
while (~scanf("%d%d%d", &n, &v, &x)) {
ln = rn = 0;
int a, b;
while (n--) {
scanf("%d%d", &a, &b);
if (a < x) l[++ln] = SB(a, b);
if (a > x) r[++rn] = SB(a, b);
}
sort(l + 1, l + ln + 1, cmp2);
sort(r + 1, r + rn + 1, cmp1);
l[0].x = r[0].x = x;
for (int i = 0; i <= ln; i++)
for (int j = 0; j <= rn; j++)
dp[i][j][0] = dp[i][j][1] = INF;
dp[0][0][0] = dp[0][0][1] = 0;
for (int i = 1; i <= ln; i++)
suml[i] = suml[i - 1] + l[i].b;
for (int i = 1; i <= rn; i++)
sumr[i] = sumr[i - 1] + r[i].b;
for (int i = 0; i <= ln; i++) {
for (int j = 0; j <= rn; j++) {
int tot = v * (suml[ln] - suml[i] + sumr[rn] - sumr[j]);
if (i != ln) {
dp[i + 1][j][0] = min(dp[i + 1][j][0], dp[i][j][0] + (l[i].x - l[i + 1].x) * tot);
dp[i + 1][j][0] = min(dp[i + 1][j][0], dp[i][j][1] + (r[j].x - l[i + 1].x) * tot);
}
if (j != rn) {
dp[i][j + 1][1] = min(dp[i][j + 1][1], dp[i][j][0] + (r[j + 1].x - l[i].x) * tot);
dp[i][j + 1][1] = min(dp[i][j + 1][1], dp[i][j][1] + (r[j + 1].x - r[j].x) * tot);
}
}
}
printf("%lld\n", min(dp[ln][rn][0], dp[ln][rn][1]));
}
return 0;
}