树形DP

概念:树形DP其实就是可以通俗的称为树上的DP,传统的递推法可以表示为“对于每个状态i,计算f(i)”,或者称为“填表法”。这需要对每个状态i,找到f(i)依赖的所有状态,在某些情况下并不方便。另一种方法是“对于每个状态i,更新f(i)所影响的状态”,也称为“刷表法”,有时比填表法方便。

举例:树的最大独立集

对于一颗n个节点的树,选出尽量多的节点,使得任意两个节点均不相邻,称为最大独立集

解答:用d(i)表示以i为根节点的子树的最大独立集大小

            节点i只有两种决策:选和不选,计算可以从孩子节点值和孙子节点的值来计算,

            但是总体来说不是很方便计算,我们可以换一种角度来看,当计算一个d(i)后,我们用它更新i的父亲节点和祖父节点的累加值。


Party at Hali-Bula

I'm going to have a party at my villa at Hali-Bula to celebrate my retirement from BCM. I wish I could invite all my co-workers, but imagine how an employee can enjoy a party when he finds his boss among the guests! So, I decide not to invite both an employee and his/her boss. The organizational hierarchy at BCM is such that nobody has more than one boss, and there is one and only one employee with no boss at all (the Big Boss)! Can I ask you to please write a program to determine the maximum number of guests so that no employee is invited when his/her boss is invited too? I've attached the list of employees and the organizational hierarchy of BCM.

代码:

#include <stdio.h>
#include <string.h>

#define N 201
#define LEN 101
char str[N][LEN];

typedef struct Edge
{
	int from;
	int to;
	int next;
}Edge;

int tol;
Edge edges[N];
int head[N];
int dp[N][2];
bool dup[N][2];

inline int max(int a, int b)
{
	return a > b ? a : b;
}

void AddEdge(int s, int e)
{
	edges[tol].from = s;
	edges[tol].to = e;
	edges[tol].next = head[s];
	head[s] = tol++;
}

void dfs(int root)
{
	int i, j;
	dp[root][0] = 0;
	dp[root][1] = 1;
	dup[root][0] = 1;
	dup[root][1] = 1;
	for (i = head[root]; i != -1; i = edges[i].next)
	{
		int u = edges[i].to;
		dfs(u);
		dp[root][0] += max(dp[u][0], dp[u][1]);
		dp[root][1] += dp[u][0];
		if (dp[u][0] > dp[u][1] && dup[u][0] == 0)
			dup[root][0] = 0;
		else if (dp[u][1] > dp[u][0] && dup[u][1] == 0)
			dup[root][0] = 0;
		else if (dp[u][0] == dp[u][1])
			dup[root][0] = 0;
		if (dup[u][0] == 0)
			dup[root][1] = 0;
	}
}

int main(void)
{
	int n;
	char str1[LEN];
	char str2[LEN];
	while (scanf("%d", &n) && n)
	{
		int i, j;
		int num = 0;
		scanf("%s", str[num++]);
		tol = 0;
		memset(head, -1, sizeof(head));
		for (i = 1; i < n; i++)
		{
			scanf("%s %s", str1, str2);
			int id1 = -1;
			int id2 = -1;
			for (j = 0; j < num; j++)
			{
				if (strcmp(str1, str[j]) == 0)
					id1 = j;
				if (strcmp(str2, str[j]) == 0)
					id2 = j;
				if (id1 != -1 && id2 != -1)
					break;
			}
			if (id1 == -1)
			{
				id1 = num;
				strcpy(str[num++], str1);
			}
			if (id2 == -1)
			{
				id2 = num;
				strcpy(str[num++], str2);
			}
			AddEdge(id2, id1);
		}
		dfs(0);
		if (dp[0][0] > dp[0][1] && dup[0][0])
			printf("%d Yes\n", dp[0][0]);
		else if (dp[0][1] > dp[0][0] && dup[0][1])
			printf("%d Yes\n", dp[0][1]);
		else
			printf("%d No\n", max(dp[0][0], dp[0][1]));
	}

	return 0;
}


            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值