对比网上各种版本的代码,主要优化为减少了无用计算,从原来的75ms降低至0ms
优化细节见代码注释
#include <bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
const int N = 205;
using namespace std;
struct edge
{
int to, next;
} e[N];
int head[N], ecnt;
void add(int u, int v) { e[++ecnt].next = head[u], e[ecnt].to = v, head[u] = ecnt; }
int n, m, sz[N]; // sz[i]记录结点i的子结点数
int dp[N][N], val[N];
void einit(int n)
{
ecnt = 0;
for (int i = 0; i <= n; i++)
head[i] = 0;
}
void getsize(int u) //计算子结点数
{
sz[u] = 1;
for (int i = head[u]; i; i = e[i].next)
{
getsize(e[i].to);
sz[u] += sz[e[i].to];
}
}
void dfs(int u, int M) // M是当前层最多能攻取的城堡数,每递归一层就减一
{
dp[u][1] = val[u];
int v, szcnt = 1;
for (int i = min(sz[u], M); i >= 2; i--)
dp[u][i] = 0;
for (int i = head[u]; i; i = e[i].next)
{
v = e[i].to;
dfs(v, M - 1);
szcnt += sz[v]; // szcnt是当前子结点的数量和,用于确定j的边界
for (int j = min(szcnt, M); j >= 2; j--) //从大的往小推,否则会出现包含关系
{
//要保证v之前的结点数之和要不小于j-k,否则解不存在
int k1 = max(sz[v] + j - szcnt, 1);
//同时k不能超过j-1或sz[v]
int k2 = min(sz[v], j - 1);
for (int k = k1; k <= k2; k++) //从结点v攻取k个城堡
dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);
}
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int a;
val[0] = 0;
for (int i = 0; i < N; i++)
dp[i][0] = 0;
while (cin >> n >> m && n && m)
{
einit(n + 1);
m++;
for (int i = 1; i <= n; i++)
{
cin >> a >> val[i];
add(a, i);
}
getsize(0);
dfs(0, m);
cout << dp[0][m] << "\n";
}
return 0;
}