CF-Round 83-Div. 2-E题
E. Array Shrinking
呃。。。这个出题人似乎很喜欢出关于序列构造的题目。。各种还是不同的算法。。
这个题目是区间dp。
一看到数据规模500。暴力肯定会超时。不用想dp就来了。
题目大概意思是让你尽可能的合并相邻的相同的元素,每次合并都要用一个比原来的数大1的数字替换。问尽可能的合并后。最终可以使得序列的长度最小为多少。
很明显用dp[i][j]记录从i~j的序列替换后的最小长度。
很显然我们需要另外一个数组保存合并后的值。
方便下次使用。
区间dp就是枚举当前区间维护最小值即可。
只有当dp[l][k] == dp[k + 1][r] == 1并且a[l][k] == a[k+1][r]的时候可以更新当前值。代表相同的相邻的两个数字可以合并。
初始化部分就是:
dp[i][j] = j - i + 1(为序列原本的长度即可)
a[i][i] = 第i个序列元素的值。
我在维护最小值的时候纠结了一下这个式子
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k +1][j]);
先开始觉得似乎没必要。。认为在满足要求的时候dp[i][j]就已经更新为1了。并且dp[i][k] + dp[k + 1][j]肯定大于等于2。
后来才发现我的这种想法是只针对序列中的每个数字都可以合并并且最终合并为一个数字的情况。。。
其他最后序列长度大于等于2的情况无法满足要求。
上述式子是维护操作后序列的最小长度。必须更新。要不然等于白操作。最后输出的还是原序列的长度。
这一点要想清楚了。
加油!
代码部分:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 5e2 + 10;
int a[N][N];
int dp[N][N];
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
dp[i][j] = j - i + 1;
}
}
for (int i = 1; i <= n; i++)
{
int temp;
cin >> temp;
a[i][i] = temp;
}
for (int len = 2; len <= n; len++)
{
for (int l = 1; l + len - 1 <= n; l++)
{
int r = l + len - 1;
for (int k = l; k < r; k++)
{
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
if (dp[l][k] == 1 && dp[k + 1][r] == 1 && a[l][k] == a[k + 1][r])
{
dp[l][r] = 1;
a[l][r] = a[l][k] + 1;
}
}
}
}
cout << dp[1][n] << endl;
return 0;
}