树形DP总结(入门6题)

1、POJ-2342

在一个公司中,每个职员有一个快乐值ai,现在要开一个party,邀请了一个员工就不可能邀请其直属上司,同理邀请了一个人就不可以邀请其的直属员工,

问如何使得这个快乐值达到最大。

显然简单树形dp,对每个结点dp[i][0]表示不邀请这个员工,其子树达到的最大快乐值,dp[i][1]表示邀请i员工其子树达到的最大值。

dp[i][0]=(i的全部员工的max(dp[u][1],dp[u][0)相加,也就是其子员工来或不来的最大快乐值。

dp[i][1]=(i的全部员工的dp[u][0相加,也就是其子员工都不能不来的最大快乐值。

从大boss开始dp,最终结果就是max(dp[root][0],dp[root][1])

同样的题目在hdu也有:
HDU 1520   Anniversary party

题目是说有N个人参加party,每个人有一个rating值(可以理解为权值)和一个up(上司的编号),为了保证party的趣味性,每一个人不可以和他的直接上司都参加,问最后的rating和最大

这是一个典型的树形DP,DP[u][0]表示u不参加那他的这棵子树上的最大权值,DP[u][1]表示u参加时的这棵树上的最大权值,

每个U节点有两种选择  参加party时的该他的树的权值(dp[u][1]),不去参加party该树的权值(dp[u][0]);

当他(u)不去时,他本身的权值(dp[u][0])就等于  他所有后面节点(儿子节点,后继节点)最大的权值(他儿子节点有两种情况,去或不去,所以是求最大值之和)dp[u][0] += max(dp[v][1], dp[v][0]);

当(u)去时,它本身的权值dp[u][1])只能加上他所有后面节点(儿子节点,后继节点)不去时的权值。dp[u][1] += dp[v][0];

v是从u读出来的 后面节点(儿子节点,后继节点)。

代码链接 https://blog.csdn.net/readlnh/article/details/52226453

Sample Input


 
7

1

1

1

1

1

1

1

1 3

2 3

6 4

7 4

4 5

3 5

0 0

 

Sample Output

5

#include
#include
#include
using namespace std;
const int maxn=6000+10;
vectorG[maxn];
int dp[maxn][2],n,u,v,f[maxn];
void dfs(int root)
{
	for(int i=0;i

2、POJ-1463

现在要在一棵树上布置士兵,每个士兵在结点上,每个士兵可以守护其结点直接相连的全部边,问最少需要布置多少个士兵。

还是简单树形dp,状态转移方程好写。

dp[i][0]表示这个点不选(说明子结点全部选),因为只有子结点全部选,才能确保这个点到子结点的全部边都有士兵守卫。

dp[i][1]表示这个点选,那么子结点就是+min(选/不选),然后对根结点一样是判断0与1的大小即可

#include
#include
#include
using namespace std;
const int maxn=1500+10;
int n,f[maxn],v,num,u,dp[maxn][2];
vectorG[maxn];
void dfs(int root)
{
	for(int i=0;i

3、POj-2378

给一一颗树,问删除哪些结点可以使得剩下的子图每一个连通分支的节点数<=总/2。

那么dp[i]表示这棵子树的下面的(包括其)的总结点数

如果一棵树其全部子树<=总/2,且总-其全部子树结点和-1<=总/2,那么这个结点就合适,用个数组来储存,最后排序即可

 这题需要注意的是,上面两个题是有向图的保存方式,这题属于无向图的保存方式,可以从任意一点出发,无需找到根节点

具体看代码:

#include
#include
#include
#include
using namespace std;
const int maxn=1e4+10;
vectorG[maxn],que;
int n,v,u;
int dfs(int root,int fa)
{
//	if(G[root].size()==1&&G[root][0]==fa) return 1;//可有可无,判断叶子节点 
	int mx=0,sum=1,u;
	for(int i=0;i

4、ZOJ-3201***(agin)

给一棵树,问这棵树大小为k的子树最大的权值和是多少。

这题稍微有点难度,dp[i][j]表示i结点长度为j(包括其)的最大权值是多少。

然后对于每个其子结点就进行背包dp,判断这个结点对于长度为l的权值是否应该要(这里需要注意的就是一个结点不能要多次,所以应该从大到小dp(类似背包),而且要一个数组储存最初状态,比如这个根结点只有长为1的链,但是这个子结点长为3,计算到2的时候这个根结点已经有长度为3的结点了,再计算子结点为3的时候,发现根结点有3长度的结点,就会出现这个根结点存在长度为6的链,显然这是错误的)。

这个题属于在树上的dp,我也是理解很久才懂,有点难度,跟01背包相似又有区别

两个代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
#define mst(s, t) memset(s, t, sizeof(s))
const int INF = 0x3f3f3f3f;
const int maxn = 110;
vector G[maxn];
int dp[maxn][maxn];  //dp[i][j]:node[i]结点数为j的子树的最大权值
int k, ans, cnt[maxn], weight[maxn];
int dfs(int node, int father)
{
    cnt[node] = 1;
    for(int i=0; i= 1; j--)
		{
            for(int t = 0; t
#include
using namespace std;
int d[105][105],a[105],n,k,ans;
vectorG[105];
void dfs(int u,int fa)
{
	d[u][1]=a[u];
	for(int i=0;i0;j--)
		for(int p=1;p+j<=k;p++)
		d[u][j+p]=max(d[u][j+p],d[u][j]+d[v][p]);
	}
	ans=max(ans,d[u][k]);
}
int main()
{
	int u,v;
	while(~scanf("%d%d",&n,&k))
	{
		for(int i=0;i

5 - Cell Phone Network

题意:给n[1,10000]个点,n-1条边,树形结构,从n个点中取尽量少的点构成一个集合,使剩下所有点都能与这个集合中的部分点相连。

(这个概念叫最小支配集),第二题应该就是最小点覆盖了

树形DP总结(入门6题)_第1张图片

这个题我不太会,待补(有点难)

F - Computer(待补,有难度)

题意:一棵边带权值的树,求每个点在树上的最远距离。

你可能感兴趣的:(dp--树形dp)