HDU1561 树形背包dp+边界优化 0ms过题

对比网上各种版本的代码,主要优化为减少了无用计算,从原来的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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值