【动态规划】SSL_1322 清兵线

题意

在一个数轴上有一些小兵,每个小兵在一个单位时间会减少一滴血量,清一个兵得到的金币为这个兵剩下的血量,求从原点如何清兵会得到尽量多的金币。

思路

因为清兵要么一直往一个方向走,要么清到一半退回来,所以我们可以设fi[i][j][k]为当前要清 k k k个兵,已经清了 i ∼ j i\sim j ij的兵,当前在第 i i i个兵上的最多金币,可以得出动态转移方程:
f i [ i ] [ j ] = m a x ( f [ i + 1 ] [ j ] + m − ( a [ i + 1 ] − a [ i ] ) ∗ ( k − j + i ) , f j [ 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])(kj+i),fj[i+1][j]+m(a[j]a[i])(kj+i)
代表继续往一个方向清,或者从 j j j折回来清。
f j fj fj的求法同理,最后在两个方法内求最大值。
具体细节结合题目和代码。

代码

#include
#include

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);
}

你可能感兴趣的:(动态规划)