题意:地图是一个编号为1~n的节点的树,节点1是敌方基地,其他叶节点是我方基地。敌人基地会出来敌人,为了防止敌人攻进我方基地,我们可以选择造塔。每个节点只能造一个塔,节点i有ki种塔供选择,价值和攻击力为price_i, power_i,攻击力power_i是让敌人经过这个节点时让敌人的HP减少power_i点。因此从敌人基地到我方任意一个基地的路径,这条路径上所有塔的攻击力之和,就是这个基地的抵抗力。 敌人攻击路径不确定,为了保护我方所有基地,需要确定所有基地中抵抗力最低的一个。我方只有数量为m的钱,问在使用最优方案时,我方能够抵挡敌人的最大HP是多少?相当于让所有基地中抵抗力最低的一个的抵御力尽量大,最大是多少?
分析:这题参考了其他人的做法,树形DP+分组背包,为了能够防守,需要从敌方基地开始攻击,这样就可以从根节点1开始状态转移。要保住我方每个基地,每个结点就要先找出容量为j时儿子结点攻击掉的最小hp,可通过把儿子结点dp[v][j](dp[i][j]表示i结点费用j时打掉的最大hp)当作一个物品,每个v对应一组背包,计算max(t,min(dp[son][j-k],dp[v][k]))获得一个组合,容量为j,里面的最小值最大,赋值给dp[i][j]。
需算出每个点i,在给予j的钱,能修建出的出的最大的稳定防御力dp[i][j],再用最小背包解决即可;对于i来说要求出的是在钱为j的前提下,最大攻击力,对于孩子,是求最大的最小防御力,最小就是按照某种方式分配钱修防御系统时,最薄弱(最小)的防御孩子是多少;最大相当于全部的方式中最大的最薄弱防御值,这样可以得到不考虑i时的dp[i][j]值,然后再在此基础上讨论i选择哪种拦截系统,就是一个简单背包dp了,该题用了两次 dp将问题简化。
#include<iostream>
#include<vector>
using namespace std;
#define N 1005
#define M 205
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
int attack[N][M]; //attack[i][j]表示节点i费用为j时的最大攻击力
int dp[N][M]; //dp[i][j]表示节点i费用为j时的能够攻击的最小攻击
int inf=0x7fffffff;
vector<int> map[N]; //树形地图
int n,m;
void initmap() //初始化地图
{
int i;
for(i=0;i<=n;i++)
map[i].clear();
}
void initdpattack() //初始化每种炮台攻击及最小抵御攻击
{
int i,j;
for(i=0;i<=n;i++)
for(j=0;j<=m;j++)
{
attack[i][j]=0;
dp[i][j]=inf;
}
}
void dfs(int u,int fa) //u代表当前节点,fa代表父节点
{
int size,i,k,j,v,minn;
size=map[u].size();
if(u!=1 && size==1) //叶子节点
{
for(i=m;i>=0;i--)
dp[u][i]=attack[u][i];
return ;
}
for(i=0;i<size;i++) //对于每一个子节点
{
v=map[u][i];
if(v==fa) continue; //排除父节点,避免回搜
dfs(v,u);
for(j=m;j>=0;j--)
{
minn=0;
for(k=0;k<=j;k++) //找到孩子节点的最小值中的最大值
{
minn=max(minn,min(dp[u][j-k],dp[v][k])); //每次分k的费用给孩子节点
}
dp[u][j]=minn; //这里没有选择根节点
}
}
for(j=m;j>=0;j--) //根节点选择了一组数据
for(k=0;k<=j;k++)
dp[u][j]=max(dp[u][j],dp[u][j-k]+attack[u][k]);
}
int main()
{
int T,i,j,u,v,num,price,power;
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>n;
initmap();
for(i=1;i<n;i++)
{
cin>>u>>v;
map[u].push_back(v);
map[v].push_back(u);
}
cin>>m;
initdpattack();
for(i=1;i<=n;i++)
{
cin>>num;
for(j=1;j<=num;j++)
{
cin>>price>>power;
attack[i][price]=max(attack[i][price],power);
}
}
dfs(1,0); //从根节点开始dfs,开始没有父节点
cout<<dp[1][m]<<endl; //输出节点1(根节点)花费为m时能够攻击的最小攻击,也就是能够打掉敌人的HP
}
return 0;
}HDU ACM 4044 GeoDefense ->树形DP+分组背包
原文地址:http://blog.csdn.net/a809146548/article/details/45695207