题意: 给定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;
}