HDU-4044 GeoDefense【树状dp+01背包】

题意

一个塔防游戏里,地图是n个结点组成的树(1-n),其中1节点是敌人的出生点,所有叶子节点是我方基地。在每个节点上都有不同的防御塔可以建造,但每个节点最多只能造一座
防御塔有 价格 和 伤害 两种属性。敌人经过一个结点时会受到该节点的防御塔的伤害数值的攻击。
给定资产 m m m,求在保证没有敌人能越过叶子节点的条件下,所能抵御的敌人的最大生命值,即使 从1节点到所有叶子节点路径上的最小的攻击总和 最大。

2
2
1 2
30
3 10 20 20 40 30 50
3 10 30 20 40 30 45
4
2 1
3 1
1 4
60
3 10 20 20 40 30 50
3 10 30 20 40 30 45
3 10 30 20 40 30 35
3 10 30 20 40 30 35
70
80




题解

  • 定义状态: d p [ i ] [ j ] dp[i][j] dp[i][j]表示在 i i i节点上,花费在 j j j以内的最大的最小攻击力和。
  • 预处理每个节点: m a x _ d m g [ i ] [ j ] max\_dmg[i][j] max_dmg[i][j]表示在i节点上花 j j j费用建塔所能得到的最大伤害效益
  • 状态转移:对于每一个非叶节点,它的状态来源是它所有的子节点。我们先得出不在它上面建塔的情况的状态,再得出建塔的状态。
  • 不建塔: d p [ x ] [ p ] = max ⁡ ( min ⁡ ( d p [ v ] [ k ] )   ∣   ∑ k = p , v ∈ s o n s ( x ) , v 1 ≠ v 2 ) dp[x][p] =\max( \min(dp[v][k])\ |\ \sum k=p,v∈sons(x), v1 \ne v2) dp[x][p]=max(min(dp[v][k])  k=pvsons(x),v1=v2)
  • 建塔:背包问题







#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pr pair<int, int>
const int maxn=4000, maxp=250;

int n, m, T, ans;

int a[maxn];
int dp[maxn][maxp];
int max_dmg[maxn][maxp];

struct node{
    int v, next;
}e[maxn*2];
int head[maxn], cnt;

void add(int u, int v)
{
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void dfs(int x, int fa)
{
    int num = 0;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int v = e[i].v;
        if(v==fa) continue;
        num++;
        dfs(v, x);
        for(int j=m;j>=0;j--)
        {
            int t = 0;
            for(int k=0;k<=j;k++)
            {
                // 先考虑x节点不建塔的情况
                t = max(t, min(dp[v][k], dp[x][j-k]));
                // dp[x][j] = max( min( dp[v][k] | sum(k) = j ) )
                // 划分j的费用给x点和它的子节点v
            }
            dp[x][j] = t;
        }
    }
    
    // 下面是在x上建塔的情况(在不建塔的情况的基础上)
    if(!num){
        dp[x][0] = 0;
        for(int i=0;i<=m;i++)
            dp[x][i] = max_dmg[x][i];
        return;
    }
    
    for(int i=m;i>=0;i--)
    {
        for(int j=0;j<=i;j++)
        {
            dp[x][i] = max(dp[x][i-j]+max_dmg[x][j], dp[x][i]);
        }
    }
}

int main()
{
    cin >> T;
    int x, y;
    while(T--)
    {
        cnt = 0;
        memset(head, -1, sizeof head);
        memset(max_dmg, 0, sizeof max_dmg);
        cin >> n;
        for(int i=0;i<n-1;i++)
        {
            cin >> x >> y;
            add(x, y);
            add(y, x);
        }
        cin >> m;
        for(int i=1;i<=n;i++)
        {
            int K;
            cin >> K;
            for(int j=0;j<K;j++)
            {
                cin >> x >> y;
                max_dmg[i][x] = max(max_dmg[i][x], y);
            }
            for(int j=1;j<=m;j++)
                max_dmg[i][j] = max(max_dmg[i][j-1], max_dmg[i][j]);
        }
        
        memset(dp, 0x3f, sizeof dp);
        dfs(1, 0);
        cout << dp[1][m] << endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值