「AGC040E」Prefix Suffix Addition 题解 (DP)

题目简介

你有一个长为 \(N\) 的序列 \(x_1,x_2,\dots,x_N\),一开始全部为 0,你现在可以以任意顺序进行任意次以下两种操作:

  1. 选定整数 \(k(1\le k\le N)\)不下降非负序列 \(c_1,c_2,\dots,c_k\),对所有 \(1\le i \le k\),令 \(x_i\) 加上 \(c_i\)
  2. 选定整数 \(k(1\le k\le N)\)不上升非负序列 \(c_1,c_2,\dots,c_k\),对所有 \(1\le i \le k\),令 \(x_{N-k+i}\) 加上 \(c_i\)

问最少进行多少次操作使得最后对任意 \(i\)\(x_i=A_i\)

转化

0x1.

由于 \(x\) 的初始值全为 \(0\) ,所以可以考虑将其翻一转,变成

  1. 选定整数 \(k(1\le k\le N)\)不上升非负序列 \(c_1,c_2,\dots,c_k\),对所有 \(1\le i \le k\),令 \(x_i\) 加上 \(c_i\)
  2. 选定整数 \(k(1\le k\le N)\)不下降非负序列 \(c_1,c_2,\dots,c_k\),对所有 \(1\le i \le k\),令 \(x_{N-k+i}\) 加上 \(c_i\)

这样,对于操作1,可视为 \(c_{k+1}\dots c_N = 0\)

对于操作2,可将\(c_{N-k+1}\dots c_N\)赋值为\(c_1\dots c_k\),然后令\(c_1\dots c_k =0\)

操作1的所有序列叠加,所得的还是不上升非负序列,记为 \(B\)

操作2的所有序列叠加,所得的还是不下降非负序列,记为 \(C\)

可得 \(A_i=B_i+C_i\),转换得 \(C_i=A_i-B_i\)\(A\)已知,DP求 \(B\) 即可。

0x2.

\(f_{i,j}\) 表示 DP 到第 \(i\) 个位置,其中 \(B_i=j\) 的最小值。

由:

\[f_{i,B_i}=\min f_{i-1,B_{i-1}}+[B_i<B_{i-1}]+[C_{i}>C_{i-1}] \]

不难得出状态转移方程:

\[f_{i,j}=\min f_{i-1,k}+[j<k]+[A_i-j>A_{i-1}-k] \]

变形:

\[f_{i,j}=\min f_{i-1,k}+[j<k]+[j<k-A_{i-1}+A_i] \]

0x3.

由上式可知,\(j\) 越大,越难满足 \(j<k\)\(j<k-A_{i-1}+A_i\)

也就是说,\(k\) 越小,越容易满足 \(j<k\)\(j<k-A_{i-1}+A_i\)

另外,对于已知状态\(f_{i-1,k}\)\(f_{i,j}\) 只可能有三种值

  • \(f_{i-1,k}\)
  • \(f_{i-1,k}+1\)
  • \(f_{i-1,k}+2\)

换句话说,如果对于 \(f_{i,j}\) ,有 \(f_{i,j'}<f_{i,j}-2\),那么 \(f_{i,j}\) 是无意义的。

0x4

偷换概念

使用 \(f_{i,j}\) 表示,当DP到前 \(i\) 位,最小值为 \(j\) 时对应的 \(B_i\) 最小值。

即 将原本的\(f_{i,j}\)概念调换一下。

使用map进行DP。

\(AC\ Code\)

#include<cstdio>
#include<iostream>
#include<map>
#include<vector>
#include<functional>
int main(){
	int n;std::cin>>n;
	std::vector<int>a(n+1);
	std::map<int,int>f[2];
	int cur=0;
	for(int i=1;i<=n;++i)std::cin>>a[i];
	std::function<void(int,int,int)>update=
	[&](int k,int x,int y){
		if(!f[k].count(x))f[k][x]=y;
		f[k][x]=std::min(f[k][x],y);
	};
	f[cur][0]=a[1],f[cur][1]=0;
	for(int i=2;i<=n;++i){
		cur^=1;f[cur].clear();
		int v1=std::max(a[i]-a[i-1],0);
		int v2=std::min(a[i]-a[i-1],0);
		for(auto it=f[cur^1].begin();it!=f[cur^1].end();++it){
			if(it->first-f[cur^1].begin()->first>2)continue;
			int x=it->first;
			int y=it->second;
			if(v1+y<=a[i])update(cur,x,std::max(v1+y,0));
			if(v2+y<=a[i])update(cur,x+1,std::max(v2+y,0));
			if(v2+y>0)update(cur,x+2,0);
		}
	}
	int ans=n;
	for(auto it=f[cur].begin();it!=f[cur].end();++it)
		ans=std::min(ans,it->first+(it->second>0));
	std::cout<<ans<<'\n';
	return 0;
}

$$-----EOF-----$$

posted @ 2022-04-01 17:42  AlienCollapsar  阅读(62)  评论(0编辑  收藏  举报
// 生成目录索引列表 // ref: http://www.cnblogs.com/wangqiguo/p/4355032.html // modified by: zzq