ツリーDP | 1: - 例:ゲーム

ゲーム

実績 10 オンタイム 2020年3月10日(火)7時55分
割引 0.8 ディスカウント時間 2020年4月7日火曜日午前23時55分
後半許可 ノー 閉会時間 2020年4月7日火曜日午前23時55分

誰かがコンピュータゲーム、特に戦略的なゲームをプレイするのが好き、私はしばしば非常に落ち込んで感じるように、時には彼ができるだけ早く解決策を見つけることができません。さて、次の質問に直面した:彼は城の道路は無向木を形成し、要塞中世の城でなければなりません。彼らはすべての側面を見ることができるように、接合部での兵士の最小値をスケジュールします。あなたは彼を助けることはできますか?

あなたの仕事は、兵士の最小数を与えています。

入力データの複数のセットを含みます。データの各セットは、データの各セットに、ツリーを表します。

最初の行は、ノードの数です。

ノードの次の数行は、以下の形式で記述されています。

ノード識別子:(道路数)ノード識別子番号2のノード識別子のノード識別子......道

若しくは

ノード識別子:(0)

N(0 <N <= 1500)ノードの場合、0からnまでの数であり、識別子ノード - 1は、整数です。各エッジは、テストケースで一度だけ表示されます。

各データセットについて、各兵士の整数所定の最小数で表されます。

  テスト入力 予想される出力 タイムリミット メモリ制限 追加のプロセス
テストケース1  
  1. 4↵
  2. 0:(1)1↵
  3. 1:(2)23↵
  4. 2:(0)↵
  5. 3:(0)↵
  6. 5↵
  7. 3:(3)1 42↵
  8. 1:(1)0↵
  9. 2:(0)↵
  10. 0:(0)↵
  11. 4:(0)↵
 
  1. 1↵
  2. 2↵
1秒 64M 0

これは、彼らがすべての側面を見ることができるように、木問題、必要な兵士の上部に複数のノードを提供します。

図1に示すように、処理の入力 - 図が記憶

接続された無向非環式グラフであるツリーのタイトル。考えてみましょ  隣接リストを 格納します。(ベクトル容器は、リストの操作を簡略化するために使用されます)

それぞれの側は一度だけ入力されます:トピックがあることを示しました。b点の後に入力された場合には、あなたが入力したAB側のために、その後のポイントは、B、Aの後に入力することはありません。このマップのタイトルを入力し、構造グラフを扱う必要があるとき、私たち一般のグラフトラバーサルの便宜のために、あなたが全体をしたい2点間のアップエッジ、無向グラフである:我々はできる、辺ABのために、ですアクセスAにB、bが通ってアクセスすることができます。

2、最適なダイナミックな展開見つける - ディープ検索

各側面を見ることが必要であることを確実にするために、その後、各辺の両端は、兵士の少なくとも一方の端部を有していなければなりません各ポイントは2つのオプションがあります、ホールド兵士兵士を置きますツリーの構造と特性:ルートの下のサブノードがルートノードと各バイトの数をノードとその子孫は、サブツリーとして見ることができます。私たちは、深い検索使用できるように:検索ルートルートを、サブツリーのルートのそれぞれの検索を継続続きます(第一ルートは選択することができます)

最小数を記録するために二次元配列DP [0..N-1、0..1]を使用して:

  • ときに必要なツリーのルートのルートにルートノードホールド兵士の最小数:DP [ルート] [0]
  • 兵士の最小数は、ルートノードを置く場合、ルートは必要なツリーのルートである:DP [ルート] [1]

この質問は、ボトムアップのソリューションでなければなりません。

最初の(次のノードを設定する)サブツリー DP [次] [0]、 [1] DP [次に] サブツリーのいずれかに基づいて算出されるように、計算される(そのノードのルートを設定)ツリー DP [ルート] [0]、 DP [ルート] [1]。最終的な答えはDPであるべきである[ルート] [0]、 DP [ルート] 小さな値[1]。

状態方程式の構築: 

だから、どのようにそれの全体の木に果物の木から移行するには?二つの方法で:

  • ルートノードホールド兵士場合:それぞれの子の兵士は次のノードを配置しなければならないことを、あなたはDP選択する必要があり、[次へ]を[1]
  • ルートノード放電兵士場合:その子ノードの各々は、次の兵士の兵士が保持することができる配置することができるDP選択されるべきである[次] [0]、ルート自体と共にDP [次]小さな値[1] A

次のようにそのため、状態遷移方程式は、次のとおり

DP [ルート] [0] = \和DP [次] [1]

\ bg_white DP [ルート] [1] = 1 + \和分(DP [次] [0]、DP [次] [1])


ACコンプリートコード:

//
// Created by LittleCat on 2020/3/10.
//

#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>

using namespace std;
#define MAX 1550

vector<int> point[MAX]; //map[i]储存节点i的临接节点集
bool vis[MAX];  //vis[i]表示节点i是否被访问过
int dp[MAX][2];

void DFS(int root) {
    vis[root] = true;   //标记已访问节点
    /* 依次遍历根节点的每一个子节点 */
    for (int i = 0; i < point[root].size(); i++) {
        int next = point[root][i];   //子节点
        if (vis[next])   //是已访问的前驱节点
            continue;  //避免往回搜索
        DFS(next);  //搜索子节点
        dp[root][0] += dp[next][1];
        dp[root][1] += min(dp[next][0], dp[next][1]);
    }
    dp[root][1]++;
}

int main() {
    int n;
    while (EOF != scanf("%d", &n)) {
        /* 初始化数组 */
        for (auto & i : point)
            i.clear();
        memset(vis, false, sizeof(vis));
        memset(dp, 0, sizeof(dp));
        /* 处理输入 */
        for (int i, k; n; n--) {
            scanf("%d:(%d)", &i, &k);  //前驱节点i,边数k
            for (int j; k; k--) {
                scanf("%d", &j);  //后继节点j
                point[i].push_back(j);
                point[j].push_back(i);
            }
        }
        int root = 0;
        DFS(root);
        printf("%d\n", min(dp[root][0], dp[root][1]));
    }
}

 


あなたが賞賛の役に立つ少しこの記事を希望する場合はご質問は、交換を確認してください持っている、姫姫〜  



「手羽先をプログラミング」パブリックには個人的な歓迎の注目は、ここに深刻な行儀のコード農業の1ではありません。

----は、最も行儀ブログERを行い、ほとんどの固体プログラマが行います----

慎重に、通常のノートに集約各記事を、書くことを目指し、更新をプッシュします -

ここに画像を挿入説明

公開された138元の記事 ウォン称賛63 ビュー10000 +

おすすめ

転載: blog.csdn.net/weixin_43787043/article/details/104970405