【ICPC】2022银川站 B. The Great Wall | 动态规划

498 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【ICPC】2022银川站 B. The Great Wall | 动态规划

题目链接

Problem - B - Codeforces

题目

Beacon towers are built throughout and alongside the Great Wall. There was once a time when there were nn beacon towers built from west to east for defending against the invaders. The altitude of the ii-th beacon tower, based on historical records, is aia_i.

The defenders divide strategically all beacon towers into kk parts where each part contains several, but at least one, consecutive beacon towers. The scale of an individual part is given by the difference between the highest and the lowest altitudes of beacon towers, and the most sensible partition maximizes the sum of scales of all parts.

As a historian, you are dying to know the maximum sums of scales for every k=1,2,,nk=1,2,…,n.

题目大意

给定含有 nn 个元素的数组,把数组划分成 kk 个段,每个段的分数是段内最大值减去最小值,最大化划分出的 kk 个段的分数之和。对 k=1,2,...,nk=1,2,...,n 分别输出答案。

思路

首先我们对问题进行转化:
给定含有 nn 个元素的数组,把数组划分成 kk 个段,第 jj 段中任取两个可以相同的数 xj,yjx_j,y_j,该段的分数是 xjyjx_j-y_j,最大化划分出的 kk 个段的分数之和。
因为我们的最终目的是最大化 kk 段分数之和,肯定要取每一段的分数最大值,必然会取到段的最大值减最小值。

转化后的问题该怎样求解呢?我们首先设状态 dp[i][j][w]dp[i][j][w] 表示前 ii 个数被划分成了 jj 段,其中第 jj 段的分数已经确定(w=0w=0)、第 jj 段的分数只取了 xjx_jw=1w=1) 和第 jj 段的分数只取了 yjy_jw=2w=2)。则转移方程是:

  • dp[i][j][0]=max(dp[i1][j][1]ai,dp[i1][j][2]+ai,dp[i1][j][0],dp[i1][j1][0])dp[i][j][0]=max({dp[i-1][j][1]-a_i,dp[i-1][j][2]+a_i,dp[i-1][j][0],dp[i-1][j-1][0]}),分别表示“aia_i 作为 yjy_j”、“aia_i作为 xjx_j”、“前 i1i-1 个元素已经完整划分了 jj 段,aia_i 对答案没有贡献”和“aia_i 自己单独划分成一段”。
  • dp[i][j][1]=max(dp[i1][j][1],dp[i1][j1][0]+ai)dp[i][j][1]=max({dp[i-1][j][1],dp[i-1][j-1][0]+a_i}),表示是否将 aia_i 作为 xjx_j
  • dp[i][j][2]=max(dp[i1][j][2],dp[i1][j1][0]ai)dp[i][j][2]=max({dp[i-1][j][2],dp[i-1][j-1][0]-a_i}),表示是否将 aia_i 作为 yjy_j

容易发现上述状态不重不漏。有效状态中 dpdp 数组可能会会取到负值,所以应当适当给数组初始化为极小值。显然 dp[i][0][0]=0dp[i][0][0]=0

则最终把长度为 nn 的数组划分成 kk 段的答案就是 dp[n][k][0]dp[n][k][0]。虽然时间复杂度是 O(n2)O(n^2),但是上界很松。 容易发现前 ii 个数的划分答案只与前 i1i-1 个数的划分答案有关,可以用滚动数组把第一维优化掉,就可以解决空间问题。

代码

#include <bits/stdc++.h>

using namespace std;
using LL=long long;
const int N=10001;
int n;
LL a[N];
LL dp[2][N][3],o;
int main() 
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for (int i=0;i<=n;++i)
		for (int j=0;j<3;++j) dp[0][i][j]=dp[1][i][j]=-1e18;
	dp[o][0][0]=dp[!o][0][0]=0;
	for (int i=1;i<=n;++i)
	{
		o=!o;
		for (int j=1;j<=i;++j)
		{
			dp[o][j][0]=max({dp[!o][j][1]-a[i],dp[!o][j][2]+a[i],dp[!o][j][0],dp[!o][j-1][0]});
			dp[o][j][1]=max({dp[!o][j][1],dp[!o][j-1][0]+a[i]});
			dp[o][j][2]=max({dp[!o][j][2],dp[!o][j-1][0]-a[i]});
		}
	}
	for (int i=1;i<=n;++i) printf("%lld\n",dp[o][i][0]);
	return 0;
}