CF-Round 83-Div. 2-E题

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值