C++---間隔 DP---追加のバイナリ ツリー (毎日のアルゴリズム 2023.4.28)

トピック:
n 個のノードを持つバイナリ ツリーの順序トラバーサルを (1,2,3,…,n) とします。ここで、数字 1,2,3,…,n はノード番号です。
各ノードにはスコア (両方とも正の整数) があり、i 番目のノードのスコアは di です。ツリーとそのサブツリーにはボーナス スコアがあります。サブツリー (ツリー自体を含む) のボーナスはポイントが計算されます。次のように:

サブツリーの左側のサブツリーのスコア × サブツリーの右側のサブツリーのスコア + サブツリーのルートのスコア サブツリーが 
空の場合、ボーナス スコアは 1 です。

リーフの追加スコアは、空のサブツリーに関係なく、リーフ ノード自体のスコアです。
順序トラバーサル (1,2,3,...,n) に準拠し、最高のスコアを持つ二分木ツリーを見つけてみます。

必要な出力: 
(1) ツリーの最高スコア 
(2) ツリーの事前順序走査

入力形式
1 行目: ノードの数を表す整数 n。 
行 2: スペースで区切られた n 個の整数。これは各ノードのスコアです (0<スコア<100)。

出力形式
行 1: 最高のボーナスとなる整数 (結果は int の範囲を超えません)。     
行 2: ツリーの事前順序走査用の、スペースで区切られた n 個の整数。複数のスキームがある場合は、辞書順が最も小さいスキームを出力します。

データ範囲
n<30

输入:
5
5 7 1 2 10
输出:
145
3 1 2 4 5
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 35;
int n, m, w[N];             //w[i]为第i个节点的分数
int f[N][N];                //f[L][R] 表示所有中序遍历的结果为L到R的所有二叉树,属性为Max(得分),
int g[N][N];                //g[L][R] 表示所有中序遍历的结果为L到R的所有二叉树中得分最高的根节点是哪个(方便最后以前序遍历输出)

void dfs(int l, int r) {
    
        //dfs获取前序遍历
    if (l > r) return;
    int root = g[l][r];
    cout << root << " ";
    dfs(l, root-1);
    dfs(root+1, r);
}

int main() {
    
    
    //读入
    cin >> n;
    for (int i =1; i<=n; i++) cin >> w[i];

    //区间dp (区间长度len -> 左端点l -> 分界线k)
    for (int len = 1; len <= n; len++) {
    
    
        for (int l = 1; l+len-1 <= n; l++) {
    
    
            int r = l+len-1;
            if (len == 1) {
    
             //初始化,所有leaf节点的值为w[i],同时leaf节点也是自己的最优根节点
                f[l][r] = w[l];
                g[l][r] = l;
            }
            else {
    
    
                for (int k = l; k<=r; k++) {
    
    
                    int left = k==l ? 1 : f[l][k-1];   //获取左子树分数,如果左子树为空记得赋值为1,要不然后续计算分数的时候乘0就不对了
                    int right = k==r ? 1 : f[k+1][r];  //获取右子树分数,同理
                    int score = right*left + w[k];
                    if (score > f[l][r]) {
    
        //f[l][r] = max(f[l][r], score), 但因为用g数组记录一下最优根节点是哪个,所以写个if
                        f[l][r] = score;
                        g[l][r] = k;
                    }
                }
            }
        }
    }

    cout << f[1][n] << endl;
    dfs(1, n);
    return 0;
}

アイデア:
インターバル dp を理解するのは難しくありませんが、事前順序/順序内/順序後トラバーサルを理解する必要があります。( 「Monster_ii ボスのバイナリ ツリーの前、中、後、層順のトラバースの詳細な図」を
参照してください。 )また、毎回最適解を保存し、最終的に dfs を出力する必要もあります。これは比較的簡単です。簡単です。コードを見てください。

古典的な y スタイルの dp メソッド
1. 状態表現
f[L][R]すべての順序走査の結果が L から R までのすべてのバイナリ ツリー (L と R を含む) であり、
属性が Max (スコア) であることを示します。

2. 状態遷移
実は、この話は非常に明確です. ルートノードを境界線 K として使用し、左の部分木と右の部分木は互いに影響しません. もちろん、部分木
f[L][R] = max(f[L][R], f[L][K-1]*f[K+1][R] + w[K])
が空の場合、スコアは 1 とみなされます。

参考になったら、無料の「いいね!」をお願いします〜 誰かが見てくれると、書く応援のモチベーションになります!

免責事項:
アルゴリズムのアイデアの出典はY氏です。詳しくはhttps://www.acwing.com/をご参照ください。
この記事は学習記録と交流のみに使用されます。

おすすめ

転載: blog.csdn.net/SRestia/article/details/130428796