Week13 作业 D - TT 的苹果树 POJ - 2342
题目描述:
TT 在门口种了一棵苹果树。
一年后,苹果熟了,到了该摘苹果的日子了。
已知树上共有 N 个节点,每个节点对应一个快乐值为 w[i] 的苹果,为了可持续发展,TT 要求摘了某个苹果后,不能摘它父节点处的苹果。
TT 想要令快乐值总和尽可能地大,你们能帮帮他吗?
思路:
- 根据苹果树的信息,总是可以建立一个树(数据结构上的树),则不难发现,该问题具有最优子结构性质,大树的最优解一定可以推出小树的最优解,则可以采用动态规划的思想
- 实现方式当然首选记忆化搜索,因为这本来就是个树,记忆化搜索的搜索树恰好和这个树重叠,为了记忆化,肯定要先求子问题(先求叶子,再向根回溯)
-
则定义状态:F[i][0]==以i为子树的根并且不选i ,F[i][1]==以i为子树并且选i
- 状态转移:F[i][0]=Σ{max(F[x][0],F[x][1]},F[i][1]=a[i]+ΣF[x][0],其中x是i的孩子节点
-
边界条件:搜到叶子,直接返回a[i]或0
- 答案:F[R][0]和F[R][1]的最大值,R是树的根(建树时树的根可以随意指定,可以回想离散数学的知识)
根据拓扑关系建立一个树(无根树转有根树):
先用邻接表存图,然后指定一个树的根,则该根节点的邻接点现在都变成了孩子节点,再递归定义这些孩子节点,直到所有节点都被这样处理一遍
vector<int> G[MAXN]; //G[i][u]=1表示i-u之间有一条边
//establish a tree,p is u's parent
void estTree(int u, int p)
{
int d = G[u].size();
for (int i = 0; i < d; ++i)
{
int v = G[u][i];
if (v != p)
{
fa[v] = u;
estTree(v, u);
}
}
}
总结:
在树上进行的DP,十分好理解,因为记忆化搜索就是树上的递归过程,可以说解空间树就是题目的树
记忆化搜索代码:
#include <cstdio> #include <iostream> #include <vector> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 6e3 + 5; int fa[MAXN], w[MAXN]; //w[i]==每个节点的权值 vector<int> G[MAXN]; //G[i][u]=1表示i-u之间有一条边 //establish a tree,p is u's parent void estTree(int u, int p) { int d = G[u].size(); for (int i = 0; i < d; ++i) { int v = G[u][i]; if (v != p) { fa[v] = u; estTree(v, u); } } } //f[i][0]==以i为子树并且不选i //f[i][1]==以i为子树并且选i //边界条件:搜到叶子,直接返回a[i]或0 bool vis[MAXN][2]; int f[MAXN][2]; int dp(int i, int j) { if (vis[i][j]) return f[i][j]; vis[i][j] = 1; if (j == 0) { int sumMax = 0; int d = G[i].size(); for (int k = 0; k < d; ++k) { int x = G[i][k]; if (x != fa[i]) sumMax += max(dp(x, 0), dp(x, 1)); } return f[i][j] = sumMax; } else { int sumMax = 0; int d = G[i].size(); for (int k = 0; k < d; ++k) { int x = G[i][k]; if (x != fa[i]) sumMax += dp(x, 0); } return f[i][j] = sumMax + w[i]; } } int main() { int N; while (cin >> N && N != 0) { memset(f, 0, sizeof(f)); memset(fa, 0, sizeof(fa)); memset(vis, 0, sizeof(vis)); for (int i = 1; i <= N; ++i) G[i].clear(); for (int i = 1; i <= N; i++) cin >> w[i]; for (int i = 1; i <= N - 1; ++i) //N个点的树,有N-1条边 { int u, v; cin >> u >> v; G[u].push_back(v); G[v].push_back(u); } estTree(1, -1); //随便取一个点为根,建树 cout << max(dp(1, 0), dp(1, 1)) << endl; } return 0; }