第一种思路:
建立森林,dp[i][k]表示森林中在第i颗树中选k个的最大价值 , dp2[j]表示选j个的最大价值,对于每颗树,都更新一次。
转移方程:
dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]);
dp2[j] = max(dp2[j],dp2[j-k]+dp[i][k]);
代码:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <vector> 5 using namespace std; 6 7 const int maxn = 200+10; 8 9 vector<int> G[maxn]; 10 int dp[maxn][maxn],dp2[maxn],a[maxn],va[maxn],n,m; 11 12 void dfs(int u){ 13 dp[u][1] = va[u]; 14 dp[u][0] = 0; 15 for(int i=0; i<G[u].size(); i++){ 16 int v = G[u][i]; 17 dfs(v); 18 for(int j=m; j>=2; j--) 19 for(int k=0; k<j; k++) // 已经选了u 所以不能再儿子里选全部j个 k<j 20 dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]); 21 } 22 } 23 24 int main(){ 25 while(scanf("%d%d",&n,&m)!=EOF){ 26 if(n==0 && m==0) break; 27 memset(dp,0,sizeof(dp)); 28 memset(dp2,0,sizeof(dp2)); 29 for(int i=0;i<maxn;i++) 30 G[i].clear(); 31 memset(a,0,sizeof(a)); 32 for(int i=1; i<=n; i++){ 33 cin >> a[i] >> va[i]; 34 if(a[i]!=0) 35 G[a[i]].push_back(i); // 形成森林 36 } 37 38 for(int i=1; i<=n; i++){ 39 if(a[i] == 0) 40 dfs(i); // 每一颗树都跑一遍01背包 dp[i][k]表示在第i颗树中选k个的最大价值 41 } 42 43 for(int i=1; i<=n; i++){ 44 if(a[i] == 0){ 45 for(int j=m; j>=0; j--) 46 for(int k=0; k<=j; k++) 47 dp2[j] = max(dp2[j],dp2[j-k]+dp[i][k]); // dp2[j]表示选j个的最大价值,对于每颗树,都更新一次 48 } 49 } 50 51 cout << dp2[m] << endl; 52 } 53 }
第二种思路:
先把依赖关系建成树,因为可能是森林,所以再建一个根节点把每个树的根节点连起来就可以。
然后树上跑dfs,在回溯的过程中跑01背包。
dp[u][i]表示在以u为根节点的子树上,选i个物品的最大价值。
然后转移方程就是dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]),就是他可以从未更新的部分选j-k个,然后在已更新的那个儿子节点那里选k个,v是当前更新到的子节点,这样把k从1到j-1扫一遍,就把这个子节点v对父节点u做的贡献做出来了。
然后这里说一下为什么j要从m到2更新,因为我们要用到dp[u][j-k],而这个代表的是未更新当前结点的情况,所以如果j从小更新的话,dp[u][j-k]就是已更新这个子节点的情况了,然后就错了,所以j要从大到小更新
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <vector> 5 using namespace std; 6 7 const int maxn = 200+10; 8 9 vector<int> G[maxn]; 10 int dp[maxn][maxn],a[maxn],va[maxn],n,m; 11 12 void dfs(int u){ 13 dp[u][1] = va[u]; 14 dp[u][0] = 0; 15 for(int i=0; i<G[u].size(); i++){ 16 int v = G[u][i]; 17 dfs(v); 18 for(int j=m; j>=2; j--) 19 for(int k=0; k<j; k++) 20 dp[u][j] = max(dp[u][j],dp[u][j-k]+dp[v][k]); 21 // for(int k=1; k<=j; k++) 22 // dp[u][j] = max(dp[u][j],dp[u][k]+dp[v][j-k]); // u选k个 子节点中选j-k个 23 } 24 } 25 26 int main(){ 27 while(scanf("%d%d",&n,&m),(n+m)){ 28 memset(dp,0,sizeof(dp)); 29 for(int i=0;i<maxn;i++) 30 G[i].clear(); 31 memset(a,0,sizeof(a)); 32 ++m; // 虚拟根节点0, 一定选 33 for(int i=1; i<=n; i++){ 34 cin >> a[i] >> va[i]; 35 G[a[i]].push_back(i); 36 } 37 38 dfs(0); 39 40 cout << dp[0][m] << endl; 41 } 42 }