洛谷 P1271 聚会的快乐(树状dp)
题目描述
你要组织一个由你公司的人参加的聚会。你希望聚会非常愉快,尽可能多地找些有趣的热闹。但是劝你不要同时邀请某个人和他的上司,因为这可能带来争吵。给定N个人(姓名,他幽默的系数,以及他上司的名字),编程找到能使幽默系数和最大的若干个人。
输入输出格式
输入格式:第一行一个整数N(N<100)。接下来有N行,每一行描述一个人的信息,信息之间用空格隔开。姓名是长度不超过20的字符串,幽默系数是在0到100之间的整数。
输出格式:所邀请的人最大的幽默系数和。
输入输出样例
输入样例#1:
5 BART 1 HOMER HOMER 2 MONTGOMERY MONTGOMERY 1 NOBODY LISA 3 HOMER SMITHERS 4 MONTGOMERY
输出样例#1:
8
这道题是一道树状dp的入门题目,树状dp的基本思想就是从某节点依次向下层递归,其实和经典的数塔问题没什么区别,这个题就是每个人可以有多个下属(每个节点可以有多个儿子)。
然后难点就是当此节点的人参加聚会时和不参加聚会时的情况处理,详细解释已经写到注释里了。
代码如下:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 #define inf 0x3f3f3f3f 5 int n,dp[110][2],value[110],son[110],vson[110][110],topnode,ans; 6 /* 7 dp[i][0]表示不让第i人参加聚会的最大值,dp[i][1]让第i个人参见聚会的最大值 8 value[i]表示第i人的幽默系数 9 son[i]表示第i个人有多少个下属 vson[i][j]表示第i人的第j个下属是谁 10 topnode 表示整个公司最上层的人,她没有上司 11 */ 12 bool vis[110][110];//记忆化搜索的标记数组 13 string namee[110],upp[110];//分别表示第i人的名字和其上司的名字 14 int dfs(int i,int j) 15 { 16 if (vis[i][j]) 17 return dp[i][j]; 18 vis[i][j]=true; 19 if (i==0) 20 return 0; 21 if (j==1)//这个人参加聚会 22 { 23 dp[i][j]=value[i]; 24 for (int k=1;k<=son[i];++k) 25 dp[i][j]+=dfs(vson[i][k],0);//他的下属都不能参加聚会,在这条件下找最大值 26 } 27 else//人不参加聚会 28 { 29 for (int k=1;k<=son[i];++k) 30 dp[i][j]+=max(dfs(vson[i][k],0),dfs(vson[i][k],1));//他的下属可以有参加或者不参加两种情况 31 } 32 return dp[i][j]; 33 } 34 int main() 35 { 36 37 //freopen("de.txt","r",stdin); 38 scanf("%d",&n); 39 memset(son,0,sizeof son); 40 memset(dp,0,sizeof dp); 41 memset(vson,0,sizeof vson); 42 memset(vis,false,sizeof vis); 43 for (int i=1;i<=n;++i) 44 cin>>namee[i]>>value[i]>>upp[i]; 45 for (int i=1;i<=n;++i) 46 { 47 bool haveUpp=false;//haveupp表示该人是否有上司 48 for (int j=1;j<=n;++j) 49 { 50 if (i==j) 51 continue; 52 if (namee[j]==upp[i]) 53 { 54 haveUpp=true; 55 vson[j][++son[j]]=i;//把上下属关系处理 56 break; 57 } 58 } 59 if (!haveUpp) 60 topnode=i; 61 } 62 ans=max(dfs(topnode,0),dfs(topnode,1));//因为最上层的人也有参加不参加两种可能 63 printf("%d\n",ans); 64 return 0; 65 }