[DP合集] 合并union

Description

给出一个 1 ∼ N 的序列 A ( A 1 , A 2 , ..., A N ) 。你每次可以将两个相邻的元素合并,合并后的元素权值即为
这两个元素的权值之和。求将 A 变为一个非降序列,最少需要多少步操作。

Input

输入的第一行一个整数 N ( N ≤ 5000) 。
接下来一行 N 个整数,描述序列 A 。保证序列 A 中的每个元素的值不超过 1000 。

Output

输出一行一个整数,表示最少的操作数。

Sample Input

5
9 7 5 13 15

Sample Output

1

题解

这是一道动态规划的题目。设\(f[i]\)表示从1到i的序列单调不降的最少操作次数,用前缀和处理一下方便取区间和。并且设$ g[i] $ 表示当操作次数最少时i点的值。首先用i从1到n循环,用j从i到1循环,如果j到i的和比第i-j位大于或等于的话,就有如下方程:
\[ f[i]=min(f[i],f[j-1]+(i-j)) \]
其中(i-j)为合并区间 [i,j] 的操作次数,同时更新\(g[i]\)。最后\(f[n]\)即为答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 302
using namespace std;
int n,i,j,k,l,a[N],sum[N],f[N],g[N];
int main()
{
    cin>>n;
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(i=1;i<=n;i++){
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
    }
    for(i=1;i<=n;i++){
        for(k=i;k>=1;k--){
            l=i;
            if(i-l+k-1>=0){
                int tmp=f[i-l+k-1]+(l-k);
                if(tmp<f[i]&&sum[l]-sum[k-1]>=g[i-l+k-1]){
                    f[i]=tmp;
                    g[i]=sum[l]-sum[k-1];
                }
            }
        }
    }
    cout<<f[n]<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LSlzf/p/10598064.html