多叉树均可
边权,取q条边使权值最大
(1)dfs求该节点u的子节点个数(==边的总数)
(2)枚举u的含当前v的子树所保留的边数,和不含v的子树保留的边数
(3)在过程中更新答案
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 200; int to[maxn],next[maxn],val[maxn];//链式前向星 int head[maxn]; int tot; int n, q; int dp[maxn][maxn]; void merge_(int u, int v, int w); int dfs(int u, int fa); int main() { int i, u, v, w; memset(dp, 0, sizeof(dp)); memset(head, 0, sizeof(head)); tot = 0; scanf("%d %d", &n, &q); for(i = 1; i < n; ++i) { scanf("%d %d %d", &u, &v, &w); merge_(u, v, w); merge_(v, u, w); } dfs(1, 0); printf("%d\n", dp[1][q]); return 0; } int dfs(int u, int fa) { int i, j, k, son = 0;//子节点数(==边的总数) for(i = head[u]; i; i = next[i]) { int v = to[i], value = val[i]; if(v == fa) continue; son += dfs(v, u) + 1;//向下搜到的子节点加上自己 for(j = min(son, q); j; --j)//从大到小dp,避免重复,类比01背包思考 for(k = j; k; --k) dp[u][j] = max(dp[u][j], dp[u][j-k]+dp[v][k-1]+value); /*第一重循环:穷举u节点子树要保留的边数 第二重循环:穷举u节点的含v的那一半子树要保留的边数 状态转移方程,dp[u][j-k]:u的另一半子树要保留的边; dp[v][k-1]+val:u的含u的子树要保留的边*/ } return son; } void merge_(int u, int v, int w) { ++tot; to[tot] = v; val[tot] = w; next[tot] = head[u]; head[u] = tot; }