hdu 1011 Starship Troopers

#本题算法:树上背包

借着本题对这几天做的树上dp做一些总结

#题目及翻译:

传送门:Problem - 1011 (hdu.edu.cn)

翻译:有一棵n个节点的树,每个点有花费bugi,每个点的实际花费为(bugi/20)(向上取整),每个点的价值是braini,求在花费不超过m的情况下的最大价值。

#题解:

树上dp的思想其实非常简单,就是把在线性数组里进行的动态规划给放到新的数据结构——树上进行(约等于没说

本题很明显就是一个典型的01背包问题,在有限的容量m下,实现最大的价值。

首先我们复习一下背包问题(线性的)

线性数据结构上的背包问题:

经典例题:洛谷p1048

传送门:P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

然后直接看状态转移方程部分:

二维:

f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);

具体含义不再赘述(我猜大家都会,不会的可以看一看p1048的题解),将这个二维的转移方程转化为树形dp的转移方程会变成三维dp,空间会爆掉,所以这个转移方程无法进行下去。

一维:

f[j] = max(f[j], f[j - w[i]] + v[i]);

这个转移方程已经优化了空间复杂度,代表了在总容量为i的时候物品价值和的最大值。

所以我们拿这个状态转移方程去修改成树形dp的状态转移方程

dp[k][j+l] = max(dp[k][j+l],dp[k][j]+dp[son][l]);

dp[i][j] 表示 以第i号节点为父节点,使用j个容量值所能含有的最大价值

状态为i点放置l个士兵,保证l+j<=m就可以

之后直接dfs下来就可以了。

#细节处理:

感谢这篇题解给我关于向上取整ceil()的启示。

hdu 1011 树型DP(依赖背包)-优快云博客

初始化记得存双向边

#代码:

#include<bits/stdc++.h>
#define MAXN 100005
#define N 105
using namespace std;
struct node 
{
    int bug;
    int brain;
}p[MAXN];
bool visited[MAXN];
vector<int> tree[MAXN];
int dp[N][N];
int n,m;
void init()
{
    
    memset(dp,0,sizeof(dp));
    memset(visited,true,sizeof(visited));
    for(int i = 0; i <= n; i++) 
    {
        tree[i].clear();
    }
    for(int i = 1; i <= n; i++)
    {
        cin>>p[i].bug>>p[i].brain;
        p[i].bug = ceil(p[i].bug/20.0);
        if(p[i].bug > m)        
        {
            visited[i] = false;
        }
    }
    for(int i = 1; i < n; i++)
    {
        int a,b;
        cin>>a>>b;
        tree[a].push_back(b);
        tree[b].push_back(a);//存双向边
    }
}
void dfs(int k)
{
    visited[k] = false;
    for(int i = p[k].bug; i <= m; i++)
    {
        dp[k][i] = p[k].brain;
    }
    for(int i = 0; i < tree[k].size(); i++)
    {
        int son = tree[k][i];
        if(!visited[son]) 
        {
            continue;
        }
        dfs(son);
        for(int j = m; j >= p[k].bug; j--)
        {
            for(int l = 1; l + j <= m; l++)
            {
                if(dp[son][l])
                {
                    dp[k][j+l] = max(dp[k][j+l],dp[k][j]+dp[son][l]);
                }
                
            }
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        if(n == -1 && m == -1)
        {
            break;
        }
        else
        {
            init();
            if(!m)
            {
                cout<<"0"<<endl; 
                continue;
            }
            dfs(1);
            cout<<dp[1][m]<<endl;
        }
        
    }
    return 0;
}

完结撒花~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值