题目大意:每个人一个分数,输入几组上下级关系的数据a、b,b是a的上级,然后约束条件是一场宴会中不能存在上下级的两个人或以上,但上级的上级不算,然后把所有人的分数相加,找到最大值,当然这里不是人越多分数越高,有可能存在一个人比所有人分数要高的情况

解题思路:第一题树形DP题,本题是看前辈们的题解写得。总的来说就是还是动态规划的那几步,拆分问题就是每次判断两个人是否为上下级关系,如果上级来了,下级一定不来;如果上级不来,下级有两种情况:来or不来。状态:我们用dp[i][0]表示i这个人不来的情况,从他到他的所有下级的分数累和最大值。同样的,dp[i][1]就表示他来得情况。转移方程:dp[i][1]+=dp[son[j]][0](son-->i的下级,j=0,1,2...),dp[i][1]+=max(dp[son[j]][1],dp[son[j]][1])。这里我觉得难点在构造这个树的关系,网上方法有很多,暂时写了这种方法

//这个方法耗时太大poj2342能过,hdu1520过不了
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

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

int dp[6010][2],n,cas[6010],vis[6010],s[6010];

void tree_dfs(int father)
{
	s[father]=1;
	dp[father][1]=cas[father];
	for(int i=1;i<=n;i++)
	{
		if(!s[i] && vis[i]==father)
		{
			tree_dfs(i);
			dp[father][1]+=dp[i][0];
			dp[father][0]+=max(dp[i][1],dp[i][0]);
		}
	}
}

int main()
{
	int i,father,a,b;
	while(~scanf("%d",&n))
	{
		for(i=1;i<=n;i++)
			scanf("%d",&cas[i]);
		memset(dp,0,sizeof(dp));
		memset(s,0,sizeof(s));
		memset(vis,0,sizeof(vis));
		father=0;
		while(scanf("%d%d",&a,&b) && (a+b))
			vis[a]=b,father=a;
		while(vis[father])
			father=vis[father];
	//	printf("%d\n",father);
		tree_dfs(father);
		printf("%d\n",max(dp[father][1],dp[father][0]));
	}
	return 0;
}
AC代码如下:
#include<stdio.h>
#include<string.h>
#define MAX_N 6010
struct node
{
	int father,son;
	int next;
}point[MAX_N];

int dp[MAX_N][2],n,value[MAX_N],list[MAX_N],ls,s[MAX_N];   //list-->为静态链表的每一个位置标号

void with(int father1,int son1)
{
	point[ls].father=father1;
	point[ls].son=son1;
	point[ls].next=list[father1];  //next-->指向兄弟结点  list-->存同一个父亲即兄弟(并且是最后出现的那个兄弟),指向上一个已存在的兄弟的链表模块中
	list[father1]=ls++;
	return ;
}
int max(int a,int b)
{
	return a>b?a:b;
}

void tree_dfs(int f)
{
	if(!list[f])   //如果这个父亲结点位置没有更新过  说明到末结点
	{
		dp[f][1]=value[f];
		dp[f][0]=0;
		return ;
	}
	int now=list[f];  //把父节点位置的链表标号给now
	dp[f][0]=0;
	dp[f][1]=value[f];
	while(now)   //找出所有兄弟位置的和的最大值
	{
		tree_dfs(point[now].son);
		dp[f][1]+=dp[point[now].son][0];
		dp[f][0]+=max(dp[point[now].son][0],dp[point[now].son][1]);
		now=point[now].next;  //找他的兄弟
	}
	return ;
}

int main()
{
	int i,a,b,t;
	int flag[MAX_N];
	while(~scanf("%d",&n))
	{
		for(i=1;i<=n;i++)
			scanf("%d",&value[i]);
		memset(list,0,sizeof(list));
	//	memset(s,0,sizeof(s));
		memset(flag,0,sizeof(flag));
		t=1;
		ls=1;
		while(scanf("%d%d",&a,&b) && a+b)
		{
			with(b,a);
		//	s[a]=t=b;
			flag[a]=1;
		}
		/*
		while(s[t])
			t=s[t];
			*/
		//两种找祖先的方法耗时一致
		while(flag[t])
			t++;
		tree_dfs(t);
		printf("%d\n",max(dp[t][1],dp[t][0]));
	}
	return 0;
}
还有一种用vetor的,目前还没学暂时不懂,不过应该是一种类似于动态数组的方法;还有一种也是建树,而且比较标准,容易理解,值得学习
这题卡了挺久的原因在于那个ls,point相当于一个每一个表,而ls则是给他标号,最后那句是list[father1]=ls++;而不是++ls,并且这句可以让父亲结点指向最小儿子结点,即如果有多个儿子的情况下,这个指针一定是指向那个儿子链表的头