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