题意:
有n座城堡,里边藏有价值val[i]的宝物,现在要去攻打这些城堡,总共能攻打m座城堡;有些城堡有前置条件,即需要攻击某个城堡a,必须已经攻占另个城堡b,求最大能获得价值。
题解:
树形dp。
一开始的图不是一颗树,我们需要将之变成一棵树,设虚拟城堡0,所以没有前置条件的城堡都需要先攻打城堡0,才能攻打。那么就能画出一颗以0为根的树。之后就是树形dp了,dp[i][j]表示i结点,剩余兵力为j(还能攻打j个城堡)的时候,能获得的最大宝物。dp[i][j]=max(dp[i][j],do[i][j-k]+dp[u][k]),其中u为i的子结点。那么对于所有的i的子结点,要使得dp[i][j]最大,就能转换成01背包问题了。
注意:由于增加了一个虚拟结点0,所以能攻打的城堡数m需要+1,即用于攻打城堡0,获得的价值val[0]=0。
代码:
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <map> #include <set> #include <vector> #include <cctype> using namespace std; const int maxn=202; int dp[maxn][maxn],val[maxn]; vector<int>e[maxn]; int n; void dfs(int i,int pre,int m) { for(int j=1;j<=m;j++)dp[i][j]=val[i]; for(int j=0;j<e[i].size();j++) { int u=e[i][j]; if(u!=pre) { dfs(u,i,m); for(int k=m;k>=1;k--) { for(int p=0;p<k;p++) dp[i][k]=max(dp[i][k],dp[i][k-p]+dp[u][p]); } } } } int main() { int m; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0)break; int i,j,k,a,b; for(i=0;i<=n;i++)e[i].clear(); for(i=1;i<=n;i++) { scanf("%d%d",&a,&val[i]); e[a].push_back(i); } dfs(0,-1,m+1); printf("%d\n",dp[0][m+1]); } return 0; }