poj2342:
题目大意:
大学要开年会,请公司的人来参加聚会,但是不希望员工和其最直接的上司同时出现在聚会上,问最多能邀请到多少人参加聚会。
解题思路:
转化成树的结构就是父亲节点和孩子节点不能同时出现。
设dp[i][0] 表示不取第i个人能得到的最大人数。
dp[i][1] 表示取第i个人能得到的最大人数。
可以得到如下的状态转移方程:
dp[i][0]+=max(dp[j][0],dp[j][1])
dp[i][1]+=dp[j][0]
其中j是i的孩子节点。
所以我们只需要对根节点进行一次dfs求出所有的dp数组所有的值。
最后结果就是 max(dp[root][1] ,dp[root][0])
poj3342是上题的升级版本,除了求出最多人数之外,还需要判断最多人数的方案是否唯一。
方案唯一性的判断方法:
第1:如果dp[root][1]==dp[root][0],则方案必不唯一。
第2:如果dp[i][1]==dp[i][0],并且dp[father][0]>=dp[father][1]。
如果存在某节点其取与不取最大值是一样的,并且其父节点在不取的时候得到最大人数。
那么,这个时候方案不唯一。
poj3342源代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const double eps=1e-8;
int n;
int dp[205][2];
int flag;
struct node
{
int father;
vector<int> child;
}p[205];
int vis[205];
void dfs(int now)
{
int flag1;
int i,size,child;
vis[now]=1;
size=p[now].child.size();
flag1=0;
for(i=0;i<size;i++)
{
child=p[now].child[i];
if(!vis[child])
{
dfs(child);
dp[now][0]+=max(dp[child][0],dp[child][1]);
dp[now][1]+=dp[child][0];
}
}
return;
}
int main()
{
freopen("in.txt","r",stdin);
int i,x,y,cnt,j;
char ch[105];
string a,b;
while(scanf("%d",&n)==1 && n)
{
map<string,int> m;
memset(p,0,sizeof(p));
cin>>a;
m[a]=1;
cnt=1;
for(i=1;i<n;i++)
{
cin>>a>>b;
x=m[a],y=m[b];
if(m[a]==0)
{
m[a]=++cnt;
x=cnt;
}
if(m[b]==0)
{
m[b]=++cnt;
y=cnt;
}
//x和y已经是映射好的值了,x是y的下属
p[y].child.push_back(x);
p[x].father=y;
}
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
dp[i][1]=1;
memset(vis,0,sizeof(vis));
dfs(1);
printf("%d ",max(dp[1][1],dp[1][0]));
//唯一性的判断,当前节点取和不取结果是一样的,
//并且其父亲节点不取,这样的话必然出现多种可能的选择
flag=1;
if(dp[1][1]==dp[1][0]) flag=0;
for(i=2;i<=n;i++)
{
if(dp[i][1]==dp[i][0] && dp[p[i].father][0]>=dp[p[i].father][1])
{
flag=0;
break;
}
}
if(flag) printf("Yes\n"); //是独一无二的
else printf("No\n"); //不是独一无二的
m.clear(); //用完之后将m清空
}
return 0;
}