国王的宴会 | ||||||
| ||||||
Description | ||||||
C国在成果破解J国破坏交通的阴谋之后,国王决定宴请各位大臣,合理制定宴请的人的名单的任务就交给了作为国王智囊团团长的你。 你知道国王喜欢热闹,所以你希望能邀请尽量多的人,但是做为直接上下级关系的两个人直接出现在宴会上的时候会显得很尴尬,所以不能同时请有上下级关系的两个人。 国王是宴会的主办方,也是这个王国的最高领袖,所以必须到场。 为了能为宴会准备的更好,你需要知道整个宴会最多可以邀请多少宾客。 | ||||||
Input | ||||||
有多组测试数据。 每组数据的第一行两个整数n(n<=1000)和m分别表示整个王国的官员的数量和上下级关系的数量。 接下来m行每行两个整数a和b表示b是a的直接下级。 处理到文件结束。 | ||||||
Output | ||||||
输出一个整数表示包括国王在内的宴会能邀请的人数最多的数量。 | ||||||
Sample Input | ||||||
5 4 5 3 3 4 4 1 4 2 | ||||||
Sample Output | ||||||
3 | ||||||
Source | ||||||
2012 Summer Contest 1 - DP | ||||||
Author | ||||||
曹振海@HRBUST |
思路:树型dp求最大独立集
1、找度为0的节点,那么就是最高的官,我们设定它为根节点,我们从它开始在树上动态规划。
2、
我们设定dp【i】【0】表示节点i属于被邀请的一个人,其子节点问题全部解决了的情况下的最大开心值。
我们设定dp【i】【1】表示节点i不属于被邀请的一个人,其子节点问题全部解决了的情况下的最大开心值。
3、
其中动态转移方程写出的思路:
u是i的子节点。
dp【i】【0】=val【i】+sum(节点i的子节点)dp【u】【1】;这里不难理解:因为节点i属于被邀请的一个人,那么其直接相连的子节点就是其当前节点的直接上下级关系,是不能够被邀请的。
dp【i】【1】=sum(节点i的子节点)max(dp【u】【1】,dp【u】【0】);因为点i不属于被邀请的一个人,那么其相连的子节点虽然与当前节点为直接上下级关系,但是其因为当前i节点已经保证不属于被邀请的一个人了,所以其子节点的状态无所厚非,直接选其中最优的即可。
4、题目要求最高长官(国王)必须参加宴会,那么答案就是dp【root】【0】;
注意的点:多组数据,注意各单位初始化
AC代码:
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
int dp[10000][2];
int degree[10000];
vector<int >mp[10000];
int vis[10000];
void DP(int now)
{
dp[now][0]=1;
dp[now][1]=0;
vis[now]=1;
for(int i=0;i<mp[now].size();i++)
{
int to=mp[now][i];
if(vis[to]==1)continue;
DP(to);
dp[now][0]+=dp[to][1];
dp[now][1]+=max(dp[to][1],dp[to][0]);
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(vis,0,sizeof(vis));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)mp[i].clear();
for(int i=1;i<=n;i++)degree[i]=0;
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
mp[x].push_back(y);
degree[y]++;
}
int root;
for(int i=1;i<=n;i++)
{
if(degree[i]==0)
{
root=i;
break;
}
}
DP(root);
printf("%d\n",dp[root][0]);
}
}