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;
}

  

 

posted @ 2020-06-11 16:38  菜鸡今天学习了吗  阅读(197)  评论(0编辑  收藏  举报