其他分享
首页 > 其他分享> > CF229D Towers 题解

CF229D Towers 题解

作者:互联网

\(\mathtt{Description:}\)

给定 \(1\) 个序列,包含 \(n\) 个数,每次可以合并相邻两个数,合并后的数为它们的和,求最少多少次操作可以将其变为 非递减 序列。

\(\mathtt{Solution:}\)

发现一个性质,就是合并的数必然为 \(1\) 个区间,那么可以按照最后合并的数是哪个区间进行分类讨论。

设 \(f(i,j)\) 表示前 \(i\) 个数,合并后最后 \(1\) 个数的区间为 \((j,i)\) ,\(p(i)\) 表示前缀和,有:

\(f(i,j)=\min\{f(i-j,k)+(j-1)\}\quad(k\le i-j\;,\;p(i)-p(i-j)\le p(i-j)-p(i-j-k)\) 。

复杂度 \(O(n^3)\) 。

固定 \(i\) 发现随着 \(j\) 的变大,转移集合也随之变大,那么 \(k\) 的值就不用再枚举了,直接加就好了。

可以预处理 \(g(i,j)\) 表示 \(\sum\limits_{k=j}^{i}\min f(i,k)\) 。

复杂度 \(O(n^2)\) 。

\(\mathtt{Code:}\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>

#define LL long long
#define PR pair<int,int>

using namespace std;

const int kN=5e3+5;
const int kInf=1e9;

int n;
int a[kN];
int f[kN][kN];
int p[kN];
int g[kN][kN];
int ans;

void Input(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		p[i]=p[i-1]+a[i];
	}
}

void Output(){
	ans=kInf;
	for(int i=1;i<=n;++i){
		ans=min(ans,f[n][i]);
	}
	printf("%d",ans);
}

int main(){
	Input();
	fill(f[0],f[0]+kN*kN,kInf);
	fill(g[0],g[0]+kN*kN,kInf);
	for(int i=0;i<2;++i){
		for(int j=0;j<2;++j){
			f[i][j]=g[i][j]=0;
		}
	}//初始化i=0/1的情况
	for(int i=2;i<=n;++i){
		int k=i-1;
		while(k>0&&p[i-1]-p[k-1]<=a[i]){
			k--;
		}//j=1的情况
		f[i][i]=i-1;//j=i的情况
		for(int j=1;j<i;++j){
			if(k<i-j){//排除无效状态
				f[i][j]=min(f[i][j],g[i-j][i-j-k]+(j-1));
			}
			while(k>0&&p[i-j-1]-p[k-1]<=p[i]-p[i-j-1]){
				k--;
			}//k的值减小,转移集合也就变大
		}
		for(int j=1;j<=i;++j){
			g[i][j]=min(g[i][j-1],f[i][j]);
		}//处理g数组
	}
	Output();
	return 0;
}

\(\mathtt{The\;End\;by\;GD.}\)

标签:Towers,题解,合并,kN,int,个数,include,mathtt,CF229D
来源: https://www.cnblogs.com/Godzillad/p/14703001.html