『动态规划』CF946G Almost Increasing Array

该博客探讨了如何使用动态规划解决一种特殊类型的序列问题,即如何通过最少的元素替换使序列变得几乎递增。作者证明了解决此问题的关键在于找到序列中元素之间的增量关系,并利用最长不下降子序列的思路来确定最小替换次数。解决方案中提到了使用树状数组来处理值域限制,从而有效地计算所需替换次数。
摘要由CSDN通过智能技术生成

P r o b l e m \mathrm{Problem} Problem

We call an array almost increasing if we can erase not more than one element from it so that the array becomes strictly increasing (that is, every element is striclty greater than every element before it).

You are given an array a a a consisting of n n n elements. You are allowed to replace any element with any integer number (and you may do so any number of times you need). What is the minimum number of replacements you have to perform in order to make the array almost increasing?


T r a n s l a t i o n \mathrm{Translation} Translation

定义一个序列 A A A A l m o s t    I n c r e a s i n g Almost\;Increasing AlmostIncreasing的,当 A A A满足从 A A A中去掉一个位置之后剩下的序列形成了一个严格递增序列。

给出一个长度为 n n n的序列,问最小需要改动多少个位置的值,使得这个序列变成 A l m o s t    I n c r e a s i n g Almost\;Increasing AlmostIncreasing的。


S o l u t i o n \mathrm{Solution} Solution

解决这道题我要知道一个结论:

  • 一串序列 { a i } \{a_i\} {ai}要单调递增需要改变的数值个数,那么我们所求的代价相当于序列 { a i − i } \{a_i-i\} {aii}中最长不下降子序列的长度。

证明:
考虑元素 a i a_i ai a j a_j aj 能够共存的条件。
显然 a i a_i ai a j a_j aj 是单调递增的两个元素,且单调递增序列中相邻两个元素之间增量至少为 1 1 1.
因此我们就有: a i − a j ≥ i − j ( i > j ) a_i-a_j\ge i-j(i>j) aiajij(i>j),则移项可以得到 a i − i ≥ a j − j a_i-i\ge a_j-j aiiajj.

所以,我们的问题就转化为了序列 { a i − i } \{a_i-i\} {aii}去掉一个数以后的最长不下降子序列。

我们设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示前i个数字中,还没有删除数字/已经删除数字的最长不下降子序列。

那么十分显然的就有: f [ i ] [ 0 ] = max ⁡ { f [ j ] [ 0 ] } + 1 ( a j − j ≤ a i − i ) f[i][0]=\max\{f[j][0]\}+1(a_j-j\le a_i-i) f[i][0]=max{f[j][0]}+1(ajjaii)
f [ i ] [ 1 ] = max ⁡ { f [ j ] [ 1 ] } + 1 ( a j − j ≤ a i − i ) f[i][1]=\max\{f[j][1]\}+1(a_j-j\le a_i-i) f[i][1]=max{f[j][1]}+1(ajjaii)
这两步和经典的最长不下降子序列相同。

对于去掉一个元素的情况,我们发现后面的所有点下标都会减去1,因此:
f [ i ] [ 1 ] = max ⁡ { f [ j ] [ 0 ] } + 1 ( a j − j ≤ a i − i + 1 , j < i − 1 ) f[i][1]=\max\{f[j][0]\}+1(a_j-j\le a_i-i+1,j<i-1) f[i][1]=max{f[j][0]}+1(ajjaii+1,j<i1)
对于值域的限制,我们用树状数组维护即可。


C o d e \mathrm{Code} Code

#include <bits/stdc++.h>

using namespace std;
const int N = 6e5;

int n, m(0), cnt(0);
int a[N], b[N], c[N], f[N][2];
map < int, int > tag;

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

void input(void)
{
	n = read();
	for (int i=1;i<=n;++i) {
		a[i] = read() - i;
		b[i] = a[i] + 1;
		c[++ m] = a[i], c[++ m] = b[i]; 
	}	
	sort(c + 1, c + m + 1);
	for (int i=1;i<=m;++i)
	{
		if (c[i] != c[i-1]) tag[c[i]] = ++ cnt;
		else if (i == 1) tag[c[i]] = ++ cnt;
	}
	for (int i=1;i<=m;++i) {
		a[i] = tag[a[i]];
		b[i] = tag[b[i]];
	}
	//for (int i=1;i<=n;++i) cout<<a[i]<<' ';cout<<endl;
	//for (int i=1;i<=n;++i) cout<<b[i]<<' ';cout<<endl;
}

struct BIT
{
	#define lowbit(i) (i & -i)
	int S[N * 10] = {};
	void add(int x, int v) {
		for (int i=x;i<=cnt;i+=lowbit(i))
			S[i] = max(S[i], v);
		return;
	}
	int ask(int x) {
		int res = 0;
		for (int i=x;i>=1;i-=lowbit(i))
			res = max(res, S[i]);
		return res;
	}
}  tree1, tree2, tree3;

void work(void)
{
	int res = 0;
	for (int i=1;i<=n;++i)
	{
		int val1 = tree1.ask(a[i]);
		int val2 = tree2.ask(a[i]);
		int val3 = tree3.ask(b[i]);
		f[i][0] = val1 + 1;
		if (val2 > 0) f[i][1] = val2 + 1;
		if (i > 2) f[i][1] = max(f[i][1], val3 + 1);
		tree1.add(a[i], f[i][0]);
		tree2.add(a[i], f[i][1]);
		if (i > 1) tree3.add(a[i-1], f[i-1][0]);
		res = max(res, max(f[i][0], f[i][1]));
	}
	cout << max(n - 1 - res, 0) << endl;
	return;
}

int main(void)
{
	input();
	work();
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值