题解 合并 union

Posted zsq259

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 合并 union相关的知识,希望对你有一定的参考价值。

合并 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

解析

这题很明显是用DP啊啊啊!!

我们设\(f[i]\)表示将序列\(1\)~\(i\)合并的最小次数,

\(g[i]\)表示将序列\(1\)~\(i\)合后的最后一个元素的权值,

那么,从\(1\)\(n\)枚举\(i\),

再从\(i\)~\(1\)枚举\(j\),表示将$ j\(~\)i\(合并成一个点,再添加到已经合并完后的序列\) 1\(~\)j$-\(1\)后面,

什么?你问我为什么要将$ j\(~\)i$合并成一个点?

仔细想一下,如果有一个\(k\),并且将\(j\)~\(k\)合并,再将\(k\)+$1 \(~\)i$合并后会使答案更优,

那么在枚举到\(k\)时,就会将当前情况统计一次,

而在枚举\(i\)时,当\(j\)枚举到\(k\)时,就会统计到这个答案了!!(口胡证明可能有点乱,自己画图理解下哈).

然后,我们考虑状态转移,

如果\(g[i-1]\)<=\(a[i]\)-\(a[j-1]\)(\(a\)为前缀和,表示\(i\)\(j\)合并后的权值,整个式子就表示\(i\)\(j\)合并后能接到\(j\)-\(1\)后面).

并且\(f[j-1]\)+\((i-j)\)<=\(f[i]\)(即次数更少,(\(i\)-\(j\))表示将\(j\)~\(i\)合并成\(1\)个点的次数),

那么我们就更新\(f[i]\)\(g[i]\),\(g[i]\)就是\(j\)~\(i\)合成的点的权值.

那么最后,\(f[n]\)即为答案.

不清楚的看代码吧:

#include<bits/stdc++.h>
using namespace std;

inline int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return f*sum;
}

int n,a[100001];
int f[100001],g[100001];

int main(){
    n=read();
    memset(f,0x3f,sizeof(f));f[0]=0;    
    for(int i=1;i<=n;i++) g[i]=read();//并没什么用但也没影响,只是存一下权值
    for(int i=1;i<=n;i++) a[i]=g[i]+a[i-1];//前缀和
    for(int i=1;i<=n;i++){
        for(int j=i;j;j--){
            if(g[j-1]<=a[i]-a[j-1]){
                if(f[j-1]+i-j<f[i]){//更新
                    f[i]=f[j-1]+i-j;
                    g[i]=a[i]-a[j-1];
                }
            }
        }
    }
    printf("%d\n",f[n]);
    return 0;
}

以上是关于题解 合并 union的主要内容,如果未能解决你的问题,请参考以下文章

使用 UNION ALL 合并 Hive 中的许多表?

sql 两表数据合并 union

UNION SQL问题[重复]

MySQL union合并查询

UNION和UNION ALL的作用和语法

合并查询union