件名アドレス:
https://www.acwing.com/problem/content/1076/
二股のリンゴの木があります。枝に二股がある場合、それは二股でなければなりません。つまり、息子が1人だけのノードはありません。この木には合計NNがありますNノード、番号111からNNNは、ルート番号でなければなりません1 11。ブランチの両端で接続されているノード番号を使用して、ブランチの位置を記述します。リンゴの木は枝が多すぎて剪定が必要です。ただし、一部の枝にはリンゴがあります。保持する必要のある枝の数を考慮して、保持できるリンゴの最大数を見つけます。ここでの予約は決勝と11を指しますポイント1が接続されています。
入力形式:
最初の行には2つの整数NNが含まれていますNとQQQはそれぞれ、ツリー内のノードの数と保持されるブランチの数を表します。次のN− 1 N−1N − 1行はブランチ情報を表し、各行には3つの整数があり、最初の2つは接続するノードの番号、3番目の番号はブランチ上のリンゴの数です。
出力形式:
出力は1行のみで、保持できるリンゴの最大数を示します。
データ範囲:
1≤Q<N≤1001≤Q<N≤1001≤Q<N≤1 0 0
N≠1 N≠1N=1個の
各ブランチ上のりんごは超えない30000 300003 0 0 0 0。
アイデアは動的計画法です。してみましょうF [i]の[j]をF [i]の[J]f[i][j]是以 i i 私は木の根です、せいぜいjjを保ちますj個のブランチでフェッチできるリンゴの最大数。それぞれの息子をルートとするサブツリーをアイテムのグループと見なします。アイテムの各グループは、いくつかの方法で取得できます。たとえば、子の合計はx 1、x 2、... X_1、x_2、です。 ..バツ1、バツ2、。。。、ときに考慮子供XS x_sバツS、f [xs] f [x_s]を計算しますf [ xS] 0、1、2、...、J − 1 0,1,2、...、j-1と見なすことができます0 、1 、2 、。。。、j−1本の枝(それがでてしまいますので、1 1ポイント1は接続されたツリーのルートであるため、i→xi \ to x私→xを取る必要があります)、したがって: fs [i] [j] =max{fs− 1 [i] [j]、max0≤k≤j− 1 fs − 1 [i] [j − k − 1 ] + f [xs] [k] + ∣(i、xs)∣} f_s [i] [j] = \ max \ {f_ {s-1} [i] [j]、\ max_ {0 \ le k \ le j-1} f_ {s-1} [i] [jk-1] + f [x_s] [k] + |(i、x_s)| \}fS[ i ] [ j ]=最大{ fS - 1[ i ] [ j ] 、0 ≤ K ≤ J - 1最大fS - 1[ i ] [ j−k−1 ]+f [ xS] [ k ]+∣ (i 、バツS)| }内の式のfs F_SfS最初のssのみが考慮されることを示しますsの子供たち。fs − 1 [i] [j] f_ {s-1} [i] [j]fS - 1[ i ] [ j ]列挙は完全にスキップされますxsx_sバツSこのサブツリーの状況。コードは0− 10-1を模倣できます0−1バックパックの書き方は、ボリュームを大から小に循環させて、「それらの考慮グループ」の次元を節約することです。コードは次のように表示されます。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, M = 2 * N;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
int f[N][N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int parent) {
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v == parent) continue;
// 先递归求解f[v]
dfs(v, u);
// 枚举树枝总数的上界(即保留不超过多少个树枝)
for (int j = m; j >= 1; j--)
// 枚举v这棵子树取多少个树枝
for (int k = 0; k < j; k++)
f[u][j] = max(f[u][j], f[u][j - k - 1] + f[v][k] + w[i]);
}
}
int main() {
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 0; i < n - 1; i++) {
int a, b, c;
cin >> a >> b >> c;
// 只知道树根但不知道边的方向,所以只能建双向边
add(a, b, c), add(b, a, c);
}
dfs(1, -1);
cout << f[1][m] << endl;
return 0;
}
時間と空間の複雑さO(NQ)O(NQ)O (N Q )。