14寒假集训GeoDefense

题目连接

题意: 给定n个节点组成的树,1为敌方基地,叶子结点为我方结点。我们可以在每个结点安放炮台,至多一炮,然后就可以打炮,每个结点有ki种炮,每种炮有一个花费和一个能量(能量对应着打掉敌人多少hp)。敌人可能往一个结点的每条分支跑,并且往哪一个分支跑是不确定的,就是说几个分支取最坏结果,最后问怎么打炮,才能使打掉的敌人hp最多。

题解:首先显然是树形dp,考虑一个节点以及它的分支。对于节点本身,是一个最多选一个的背包。而对于分支,是一个分组背包,组内只能选一种花费情况,并且组与组之间的关系是取最小,注意边界条件。本题特殊点还有存在价格为0的塔,那么所有的dp扫一遍都必须利用一个辅助数组。

重点:拿到节点+分支结构后判断出两种背包,正确去写。初始化。利用tmp数组。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 1e3 + 10;
const int MIN = -1e8;
const int INF = 1e8;
int dp[maxn][210];
int tmp[210];
int last_dp[210];
int n, tot, num[maxn], price[maxn][60], power[maxn][60];
vector<int> G[maxn];

void dfs(int u, int fa)
{
    if(G[u].size() == 1 && G[u][0] == fa)//后面发现不得不分,这是叶子节点的情况
    {
        REP_D(i, 1, tot)
        {
            tmp[i] = dp[u][i] = MIN;//初始化,只有花0有意义
        }
        tmp[0] = 0;
        dp[u][0] = 0;//什么都不选。
        REP_D(i, 0, tot)//只能选一个的背包。
        {
            for(int j = 1; j <= num[u]; j++)
            {
                if(i >= price[u][j] && tmp[i - price[u][j]] != MIN)
                {
                    dp[u][i] = max(dp[u][i], tmp[i - price[u][j]] + power[u][j]);//用tmp来
                }
            }
        }
        return;
    }
    REP(i, 0, G[u].size())//从这里开始是非叶子节点,首先是处理分支,这是个特殊的分组背包
    {
        int v = G[u][i];
        if(v == fa)
            continue;
        dfs(v, u);//这样的目的是防止tmp共用导致混乱
    }
    for(int i = 0; i <= tot; i++)//初始化,dp是要更新的,每组必定要选一个设为MIN,而每组只能选一个,用tmp(并且有花费0的存在)。而第一次的tmp设为INF正是分//开叶子节点的原因
    {
        dp[u][i] = MIN;
        tmp[i] = INF;
    }
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v == fa)
            continue;
        for(int j = 0; j <= tot; j++)
        {
            for(int k = 0; k <= j; k++)
            {
                dp[u][j] = max(dp[u][j], min(tmp[j - k], dp[v][k]) );//更新i,用本分支消耗k来,那么就是这样(目前还没算节点的)。
            }
        }
        for(int j = 0; j <= tot; j++)</a>//更新tmp,初始化dp
        {
            tmp[j] = dp[u][j];
            dp[u][j] = MIN;
        }
    }
    for(int i = 0;i <= tot;i++)//很关键,下面要搞节点,在已有的dp值基础上,很关键。
    {
        dp[u][i] = tmp[i];
    }
    for(int i = 0; i <= tot; i++)
    {
        for(int j = 1; j <= num[u]; j++)
        {
            if(i >= price[u][j] && tmp[i - price[u][j]] != MIN)
            {
                dp[u][i] = max(dp[u][i], tmp[i - price[u][j]] + power[u][j]);
            }
        }
    }
}

void solve()
{
    dfs(1, 0);
    int ans = 0;
    for(int i = 0; i <= tot; i++)
    {
        ans = max(ans, dp[1][i]);
    }
    printf("%d\n", ans);
}

int main()
{
    freopen("13Min.txt", "r", stdin);
    //freopen("1out.txt", "w", stdout);
    int ncase;
    scanf("%d", &ncase);
    while(ncase--)
    {
        scanf("%d", &n);
        REP_D(i, 1, n)
        {
            G[i].clear();
        }
        REP_D(i, 1, n - 1)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        scanf("%d", &tot);
        REP_D(i, 1, n)
        {
            scanf("%d", &num[i]);
            REP_D(j, 1, num[i])
            {
                scanf("%d%d", &price[i][j], &power[i][j]);
            }
        }
        solve();
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值