【模板】洛谷P2015_树形dp(带边权)_链式前向星+dfs

多叉树均可

边权,取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;
}







猜你喜欢

转载自blog.csdn.net/jay__bryant/article/details/80213774
今日推荐