题意:地图是一个编号为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