Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6875 Accepted Submission(s): 4037
3 2 0 1 0 2 0 3 7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2 0 0
5 13
解题思路:
首先,城堡可以向他的前提条件城堡连一条边,就形成了一棵树,(0点也算是树中的点,但是不算前提城堡),dp[i][j]表示i点在子树上攻占j个城堡所能获得价值的最大值,dp[0][m]即为所求。
然后遍历i的所有子节点t,每个t递归的求出所有dp[t][*]值之后,在当前所有遍历过的节点范围内求dp[i][j](就像是01背包+滚动数组那样,来求当前代价是j的最大价值,但是不同的地方是01背包当前点的价值和代价是确定的,但这里,t节点的代价和价值会改变,所以要多一层循环k,枚举不同代价带来的价值),dp[i][j]=max(dp[i][j],dp[i][k]+dp[t][j-k])(k<j)由于k<j的dp[i][k]都需要,所以j要倒着遍历,否则会过早改变值。
#include<stdio.h> #include<map> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<iostream> #define maxn 250 #define c(a) memset(a,0,sizeof(a)) #define c_1(a) memset(a,-1,sizeof(a)) using namespace std; vector<int>cl[maxn]; int dp[maxn][maxn],n,m; void dfs(int i) { for (int j = 0; j < cl[i].size(); j++) { int t = cl[i][j]; if (cl[t].size() > 0)dfs(t); for (int j = m; j > 1; j--) { for (int k = 1; k < j; k++) dp[i][j] = max(dp[i][j], dp[i][k] + dp[t][j - k]); } } } int main() { while (scanf("%d%d", &n,&m)&&n&&m) { c(dp); m++; for (int i = 0; i <= n; i++)cl[i].clear(); int a, b; for (int i = 1; i <= n; i++) { scanf("%d%d", &a, &b); cl[a].push_back(i); for (int j = 1; j <= m; j++)dp[i][j] = b; } dfs(0); printf("%d\n", dp[0][m]); } }