CF1012C 山脉
1 CF1012C 山脉
2 题目描述
时间限制 \(1s\) | 空间限制 \(512M\)
欢迎来到 \(Innopolis\)。整个一年中,\(Innopolis\) 的公民都承受着永恒的城市的建设之苦。
在房间的窗户上,可以看到 \(n\) 个山丘的序列,其中第 \(i\) 个山峰的高度为 \(a_i\)。\(Innopolis\) 政府希望在山上建造一些房屋。但是,出于城市外观的考虑,房屋只能建在严格高于附近山丘(如果有)的山丘上。例如,如果高度顺序是 \(5,4,6,2\),那么房屋只能建在高度为 \(5\) 和 \(6\) 的山丘上。
\(Innopolis\) 政府拥有一台挖掘机,可以将任意山丘的高度在一个小时内降低一个高度。挖掘机一次只能在一个小山上工作。可以将山坡降低到零高度,甚至降低到负值。挖掘机不可能增加任何山丘的高度。市政府要建造 \(k\) 栋房屋,因此必须至少有 \(k\) 座符合上述条件的山丘。调整山丘以实现政府计划所需的最短时间是多少?
但是,因为 \(k\) 的数值没有确定,需要计算在范围 \(1 ≤ k ≤ \left\lceil\frac{n}{2}\right\rceil\) 内所有k的答案?这里 \(\left\lceil\frac{n}{2}\right\rceil\) 表示 \(n\) 除以 \(2\) 并四舍五入。
3 题解
设 \(dp_{i, j, 0/1}\) 表示前 \(i\) 个数中选取了 \(j\) 个满足要求的,其中第 \(i\) 个数选取 \((1)\) 或者不选取 \((0)\) 要求最少需要多少次操作。我们先来考虑初值:前 \(0\) 个数中选取 \(0\) 个满足要求的且不选取第 \(0\) 个数肯定是不需要任何操作的,所以 \(dp_{0, 0, 0} = 0\)。前 \(1\) 个数中选取 \(0\) 个满足要求的数,且第 \(1\) 个数不选取,肯定也不需要任何操作,所以 \(dp_{1, 0, 0} = 0\)。而前 \(1\) 个数中选取 \(1\) 个满足要求的数,且第 \(1\) 个数选取也不需要操作:我们由于在遍历到这里的时候无法知道接下来的数是多少,有一些需要花费的操作就留给后面的数花费,所以 \(dp_{1, 1, 1} = 0\)。
我们接下来考虑转移:一共有两种情况,第 \(i\) 位不取或者取。
对于第一种情况,\(dp_{i, j, 0} = min(dp_{i-1, j, 0}, dp_{i-1, j, 1} + max(0, a_i - a_{i-1} + 1))\)。\(dp_{i-1, j, 0}\) 代表的是第 \(i-1\) 个数不取的情况。在这种情况下,我们没有任何需要满足的条件,直接将操作数顺承过来就好了。而 \(dp_{i-1, j, 1}\) 是指第 \(i-1\) 个数取的情况。这种情况下我们就必须让第 \(i\) 个数小于第 \(i-1\) 个数,而使第 \(i\) 个数比第 \(i-1\) 个数小的操作数便是 \(a_i - a_{i-1} + 1\)。由于第 \(i\) 个数可能本来就比第 \(i-1\) 个数小,所以我们要与 \(0\) 取 \(max\)。
对于第二种情况,\(dp_{i, j, 1} = min(dp_{i-2, j-1, 0} + max(0, a_{i-1} - a_i + 1), dp_{i-2, j-1, 1} + max(0, max(a_{i-1} - a_{i-2} + 1, a_{i-1} - a_i + 1)))\)。\(dp_{i-2, j-1, 0}\) 代表前 \(i-2\) 个数中选了 \(j-1\) 个,且不选第 \(i-2\) 个数。这代表着,第 \(i-1\) 个数不用小于等于第 \(i-2\) 个数。但是由于我们在第二种情况中必须要选第 \(i\) 个数,所以第 \(i-1\) 个数必须要比第 \(i\) 个数小,操作数就要加上 \(max(0, a_{i-1} - a_i + 1)\)。而 \(dp_{i-2, j-1, 1}\) 代表我们必须选第 \(i-2\) 个数,也就是说第 \(i-1\) 个数既要小于第 \(i-2\) 个数小,又要比第 \(i\) 个数小。此时第 \(i-1\) 个数只要小于他们的最小值即可,所以操作数加上 \(max(0, max(a_{i-1} - a_{i-2} + 1, a_{i-1} - a_i + 1))\)。
这里我们默认第 \(0\) 个数是一个极大值,因为这样就保证 \( a_{i-1} - a_{i-2} + 1\) 比 \(a_{i-1} - a_i + 1\) 小,从而不会影响答案。
4 代码(空格警告):
#include <iostream>
#include <cstring>
using namespace std;
const int N = 5e3+10;
int n;
int a[N], dp[N][N][3];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
memset(dp, 0x3f, sizeof(dp));
dp[0][0][0] = dp[1][0][0] = dp[1][1][1] = 0;
a[0] = 2147483647;
for (int i = 2; i <= n; i++)
{
dp[i][0][0] = 0;
for (int j = 1; j <= (i+1)/2; j++)
{
dp[i][j][0] = min(dp[i-1][j][0], dp[i-1][j][1] + max(0, a[i] - a[i-1] + 1));
dp[i][j][1] = min(dp[i-2][j-1][1] + max(0, max(a[i-1] - a[i] + 1, a[i-1] - a[i-2] + 1)), dp[i-2][j-1][0] + max(0, a[i-1] - a[i] + 1));
}
}
for (int i = 1; i <= (n+1)/2; i++)
{
cout << min(dp[n][i][0], dp[n][i][1]) << " ";
}
return 0;
}