题意
在一个数轴上有一些小兵,每个小兵在一个单位时间会减少一滴血量,清一个兵得到的金币为这个兵剩下的血量,求从原点如何清兵会得到尽量多的金币。
思路
因为清兵要么一直往一个方向走,要么清到一半退回来,所以我们可以设fi[i][j][k]为当前要清kkk个兵,已经清了i∼ji\sim ji∼j的兵,当前在第iii个兵上的最多金币,可以得出动态转移方程:
fi[i][j]=max(f[i+1][j]+m−(a[i+1]−a[i])∗(k−j+i),fj[i+1][j]+m−(a[j]−a[i])∗(k−j+i)fi[i][j]=max(f[i+1][j] + m - (a[i + 1] - a[i]) * (k - j + i), fj[i + 1][j] + m - (a[j] - a[i]) * (k - j + i)fi[i][j]=max(f[i+1][j]+m−(a[i+1]−a[i])∗(k−j+i),fj[i+1][j]+m−(a[j]−a[i])∗(k−j+i)
代表继续往一个方向清,或者从jjj折回来清。
fjfjfj的求法同理,最后在两个方法内求最大值。
具体细节结合题目和代码。
代码
#include<cstdio>
#include<algorithm>
int n, m, ans;
int a[301], fi[301][301], fj[301][301];
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
std::sort(a + 1, a + n + 1);
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++)
fi[i][i] = fj[i][i] = m - abs(a[i]) * k;
for (int len = 2; len <= k; len++)
for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1;
fi[i][j] = std::max(fi[i + 1][j] + m - (a[i + 1] - a[i]) * (k - j + i), fj[i + 1][j] + m - (a[j] - a[i]) * (k - j + i));
fj[i][j] = std::max(fj[i][j - 1] + m - (a[j] - a[j - 1]) * (k - j + i), fi[i][j - 1] + m - (a[j] - a[i]) * (k - j + i));
}
for (int i = 1; i + k - 1 <= n; i++)
ans = std::max(std::max(fi[i][i + k - 1], fj[i][i + k - 1]), ans);
}
printf("%d", ans);
}