問題#
二叉苹果树#
問題の説明#
リンゴの木があります。もし枝が分かれている場合、それは必ず二叉に分かれます(つまり、子ノードが 1 つだけのノードは存在しません)。
この木には合計 $N$ 個のノード(葉ノードまたは枝の分岐点)があり、番号は $1 \sim N$ です。木の根の番号は必ず $1$ です。
私たちは、枝の両端に接続されたノードの番号を使って、枝の位置を表現します。以下は、$4$ 本の枝を持つ木の例です:
2 5
\ /
3 4
\ /
1
現在、この木の枝が多すぎるため、剪定が必要です。しかし、いくつかの枝にはリンゴがなっています。
保持する必要がある枝の数が与えられたとき、最大でどれだけのリンゴを残せるかを求めてください。
解法#
$f [i][j]$ を、ノード $i$ を根とする部分木で $j$ 本の枝を保持することで得られる最大のリンゴの数と定義します。
その後、木の動的計画法を使用します。
ただし、この問題は二叉木なので、まず DFS を使って各ノードの左部分木と右部分木を処理し、その後 DP の際に直接使用できます。そうでなければ、ノード $i$ の部分木を列挙して逆順にループする必要があります。
AC コード#
#include<bits/stdc++.h>
using namespace std;
int n, q;
int read()
{
int f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return f * x;
}
const int N = 210;
int head[N], to[N], nxt[N], edge[N], cnt = 1;
int f[N][N], size[N]/*ノードiを根とする部分木に含まれる枝の数*/, lf[N], rf[N], lf_w[N], rf_w[N];
void add(int x, int y, int z)
{
to[++cnt] = y;
edge[cnt] = z;
nxt[cnt] = head[x];
head[x] = cnt;
}
void tongji(int u, int fa)
{
for(int i = head[u]; i; i = nxt[i])
{
if(to[i] == fa) continue;
tongji(to[i], u);
size[u] += size[to[i]] + 1;
if(lf[u] == 0) lf[u] = to[i], lf_w[u] = edge[i];
else rf[u] = to[i], rf_w[u] = edge[i];
}
}
int dp(int u, int j)
{
if((!lf[u]) && (!rf[u])) return 0;
if(f[u][j]) return f[u][j];
if(j <= 0) return 0;
int a = min(size[u], j);
//左の子に分ける
for(int k = 0;k <= a;k++)
{
// cout << u << " give left son " << k << "branches" << endl;
if(k == 0)
{
f[u][j] = max(f[u][j], dp(rf[u], a - 1) + rf_w[u]);
// cout << dp(rf[u], a - 1) + rf_w[u] << endl;
}
else if(a == k)
{
f[u][j] = max(f[u][j], dp(lf[u], a - 1) + lf_w[u]);
// cout << dp(lf[u], a - 1) + lf_w[u] << endl;
}
else
{
f[u][j] = max(f[u][j], dp(lf[u], k - 1) + dp(rf[u], a - k - 1) + lf_w[u] + rf_w[u]);
// cout << dp(lf[u], k - 1) + dp(rf[u], a - k - 1) + lf_w[u] + rf_w[u] << endl;
}
}
// cout << u << " " << j << ": " << f[u][j] << endl;
return f[u][j];
}
int main()
{
n = read(), q = read();
int x, y, z;
for(int i = 1;i <= n - 1;i++)
{
x = read(), y = read(), z = read();
add(x, y, z);
add(y, x, z);
}
tongji(1, 0);
dp(1, q);
cout << f[1][q];
return 0;
}