树形dp(HDU-6820 Tree)

题目连接
题意:给一颗带边权的树,你要求一个最大权联通快,要求里面超过k度的节点只能有一个。
题解:
这个题解不是官方思路。而是一位大佬的思路
首先,我们dp[i][2]表示第i个节点为根的子树。有没有与fa向连的最大权值。我们不妨先考虑,没有大于k度的节点,然后再对于每一个节点,令它的度数大于k,从而求出最大权。
显然对于dp[i][0],他最多可以连接k个孩子,对于dp[i][1],他最多可以连接k-1个孩子。我们首先要择优选取。我们首先递归孩子,得出孩子节点的最大权值,再以(孩子的最大权值+这条边的权值)从大到小排序。dp[i][0] 等于前k个孩子和边权之和。dp[i][1]即为前k-1个。然后我们思考枚举其他的边。这里从上往下枚举。累计更换某个边后的权值之和。变更边时,应该从已选边里找一个最少的,然后加新边。递归结束后,我们让这个节点为度数大于k的点,所以我们直接吧他所有的孩子再累计上即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include <map>
#include <queue>
#include <set>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <bitset>
#include <array>
#include <cctype>
#include <time.h>

#pragma GCC optimize(2)

void read_f() { freopen("1.in", "r", stdin); freopen("1.out", "w", stdout); }
void fast_cin() { std::ios::sync_with_stdio(false); std::cin.tie(); }
void run_time() { std::cout << "ESC in : " << clock() * 1000.0 / CLOCKS_PER_SEC << "ms" << std::endl; }
template <typename T>
bool bacmp(const T & a, const T & b) { return a > b; }
template <typename T>
bool pecmp(const T & a, const T & b) { return a < b; }

#define ll long long
#define ull unsigned ll
#define _min(x, y) ((x)>(y)?(y):(x))
#define _max(x, y) ((x)>(y)?(x):(y))
#define max3(x, y, z) ( max( (x), max( (y), (z) ) ) )
#define min3(x, y, z) ( min( (x), min( (y), (z) ) ) )
#define pr(x, y) (make_pair((x), (y)))
#define pb(x) push_back(x);
using namespace std;

const int N = 5e5+5;
ll dp[N][2], ans;
int n, k;
vector< pair<ll, int> > g[N], v[N];
void init(int n)
{
    for (int i = 1; i <= n; i++)
    {
        g[i].clear();
        v[i].clear();
        dp[i][0] = dp[i][1] = 0;
    }
    ans = 0;
}
void dfs(int x, int fa)
{
    int sz = g[x].size();
    for (auto i : g[x])
    {
        int y = i.second;
        ll w = i.first;
        if (y == fa) continue;
        dfs(y, x);
        v[x].pb( pr(dp[y][0] + w, y) );
    }
    sort(v[x].begin(), v[x].end(), bacmp<pair<ll, int> >);
    for (int i = 0; i < min((int)v[x].size(), k-1 + (x==1) ); i++)
        dp[x][0] += v[x][i].first;
    for (int i = 0; i < min((int)v[x].size(), k); i++)
        dp[x][1] += v[x][i].first;
}
void dfs2(int x, int fa, ll s)
{
    for (int i = 0; i < v[x].size(); i++)
    {
        int y = v[x][i].second;
        ll w = v[x][i].first;
        if (x == 1)
        {
            if (i >= k) 
                dfs2(y, x, s+dp[x][0] - v[x][k-1].first + w - dp[y][0]);
            else
                dfs2(y, x, s+dp[x][0] - dp[y][0]);
        }
        else
        {
            if (i >= k)
                dfs2(y, x, max(s+dp[x][0]-v[x][k-2].first + w -dp[y][0], dp[x][1] - v[x][k-1].first + w - dp[y][0]));
            else if (i == k - 1)
                dfs2(y,x, max(s + dp[x][0] - v[x][k-2].first + w - dp[y][0], dp[x][1]-dp[y][0]));
            else
                dfs2(y,x,max(s + dp[x][0]-dp[y][0], dp[x][1] - dp[y][0]));
        }
    }
    ll sum = 0;
    for (int i = 0; i < v[x].size(); i++)
        sum += v[x][i].first;
    ans = max(ans, sum+s);
}
int main()
{
    int t; cin >> t;
    while(t--)
    {
        scanf("%d%d", &n, &k);
        init(n);
        for (int i = 1; i < n; i++)
        {
            int x, y; ll w;
            scanf("%d%d%lld", &x, &y, &w);
            g[x].pb(pr(w, y));
            g[y].pb(pr(w, x));
        }
        if (k == 0) { puts("0"); continue; }
        else if (k == 1)
        {
            for (int i = 1; i <= n; i++)
            {
                ll sum = 0;
                for (auto j : g[i])
                    sum += j.first;
                ans = max(ans, sum);
            }
            printf("%lld\n", ans);
            continue;
        }
        dfs(1, 0); dfs2(1, 0, 0);
        printf("%lld\n", ans);

    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值