表示制杖,参考了一下别人的题解,做完发现自己整个人都不好了,竟然没有发现这么弱的dp方程,(我不知怎么的,一开始居然想了一个n^3的dp方程)
设dp[i][s]表示计算到第i个且将其放在该段的最后一个,这一段全为s(0和1分别表示两种运动)
那么dp方程就是dp[i][s] = min{dp[j][s^1] + valueSum(j+1, i)}, j < i
valueSum(i, j)表示[i, j]区间内的员工到最近运动室的距离和
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 4005; const int INF = 0x3f3f3f3f; int n; long long a[2][MAXN], sum[2][MAXN], value[2][MAXN], dp[MAXN][2]; long long leftCal(int l, int r, int s) { long long x = value[s][r] - value[s][l-1]; long long y = sum[s][r] - sum[s][l-1]; return abs(x - y * (l - 1)); } long long rightCal(int l, int r, int s) { long long x = value[s][r] - value[s][l-1]; long long y = sum[s][r] - sum[s][l-1]; return abs(x - y * (r + 1)); } long long areaDist(int l, int r, int s) { if (l == 1) return rightCal(l, r, s); else if (r == n) return leftCal(l, r, s); else { int mid = (l + r) >> 1; return leftCal(l, mid, s) + rightCal(mid + 1, r, s); } } int main() { int T; scanf("%d", &T); for (int kk = 1; kk <= T; kk++) { //input scanf("%d", &n); sum[0][0] = sum[1][0] = 0LL; value[0][0] = value[1][0] = 0LL; for (int i = 1; i <= n; i++) { scanf("%lld%lld", &a[0][i], &a[1][i]); sum[0][i] = sum[0][i-1] + a[0][i]; sum[1][i] = sum[1][i-1] + a[1][i]; value[0][i] = value[0][i-1] + a[0][i] * i; value[1][i] = value[1][i-1] + a[1][i] * i; } //initialize memset(dp, INF, sizeof(dp)); for (int i = 1; i < n; i++) { for (int s = 0; s <= 1; s++) dp[i][s] = areaDist(1, i, s); } //dp for (int i = 1; i <= n; i++) { for (int s = 0; s <= 1; s++) { for (int j = 1; j < i; j++) dp[i][s] = min(dp[i][s], dp[j][s^1] + areaDist(j + 1, i, s)); } } //output printf("Case #%d: %lld\n", kk, min(dp[n][0], dp[n][1])); } return 0; }