POJ - 3342 Party at Hali-Bula (树形DP+STL)
Dear Contestant,
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.
Best,
–Brian Bennett
P.S. I would be very grateful if your program can indicate whether the list of people is uniquely determined if I choose to invite the maximum number of guests with that condition.
Input
The input consists of multiple test cases. Each test case is started with a line containing an integer n (1 ≤ n ≤ 200), the number of BCM employees. The next line contains the name of the Big Boss only. Each of the following n-1 lines contains the name of an employee together with the name of his/her boss. All names are strings of at least one and at most 100 letters and are separated by blanks. The last line of each test case contains a single 0.
Output
For each test case, write a single line containing a number indicating the maximum number of guests that can be invited according to the required condition, and a word Yes or No, depending on whether the list of guests is unique in that case.
Sample Input
6
Jason
Jack Jason
Joe Jack
Jill Jason
John Jack
Jim Jill
2
Ming
Cho Ming
0
Sample Output
4 Yes
1 No
- 题目大意:
某人要举办一个聚会,然后要邀请公司的人,要避免员工和他的直系上司同时出现,问最多能邀请多少个人。
抽象一下模型:有一颗树,相连接的点不能同时取,问,最多能取多少个点,还要判断一下取的点的方式是否唯一。
解释一下输入输出:先输入n (顶点的个数)然后输入boss 的名字(根节点) 然后下面n- 1 行每一行输入两个名字 a ,b b是a 的直系上司。输出最多能邀请的人的个数。如果选点唯一,输出YES 否则 NO。 - 解题思路:
我们考虑一个结点,如果这个点不取的话,那么他的子结点就可以取,或者不取两种选择;如果这个点取,那么他的子节点就一定不能取。
我们用动态规划的思想来考虑这个问题,建立一个dp[][]二维数组,dp[i][0]表示,不选i结点时所能取到的最大个数(最优解) dp[i][1]就是选 i结点时的最优解,所以我们可以得出状态转移方程了:
//t是x的子节点
dp[x][0]+=max(dp[t][0],dp[t][1]);//x不选的时候 子节点可以选也可以不选;
dp[x][1]+=dp[t][0];//x选的时候 子节点一定不能选
-具体实现细节:
1.我们用邻接表来存图,因为我们要存的是一棵树,树的边数是n-1 是个稀疏图,用邻接表可以大大降低时间复杂度。
2.我们用个map 来将人名和标号联系起来。
3.用个set 来放人名,输入的时候每次遇到一个名字就判断set里有没有,没有就放进去,然后mp[str]=cnt++;
(2,3配合,来将人名标号化为了方便下面的操作。)
4.如何判断是否唯一呢? 我们想一下哈,如果dp[i][0]>dp[i][1], 那么这个时候该结点不取, 那他的子节点就有两种选择,选或者不选,如果选,不选的值一样,那选不选就都可以了 就有两种选择了,然后我们遍历每一个点,找dp[i][0]>dp[i][1]的点,找他的子节点是否存在 dp[j][0]==dp[j][1]的情况,存在就标记个flag 然后跳出
for(int i=1;i<=n;i++)
{
if(dp[i][0]>=dp[i][1])
{
for(int j=head[i];j!=-1;j=side[j].next)
{
int t=side[j].to;
if(dp[t][0]==dp[t][1])
{
flag=0;
break;
}
}
}
if(flag==0)
break;
}
这个题主要用了树形DP的思想,然后细节方面处理要用到STL。
- AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <set>
#include <cmath>
using namespace std;
const int maxn=210;
struct node{
int to;
int next;
}side[maxn*2];
int head[maxn];
int dp[maxn][2];
int cnt=0;
int n;
set<string> s;
map<string,int> mp;
void init()
{
memset(head,-1,sizeof(head));
s.clear();
cnt=0;
}
void add(int x,int y)
{
side[cnt].to=y;
side[cnt].next=head[x];
head[x]=cnt++;
}
void dfs(int x)
{
if(head[x]==-1)
{
dp[x][1]=1;
dp[x][0]=0;
return;
}
dp[x][0]=0;
dp[x][1]=1;
for(int i=head[x];i!=-1;i=side[i].next)
{
int t=side[i].to;
dfs(t);///让子节点都安排正确
dp[x][0]+=max(dp[t][0],dp[t][1]);//x不选的时候 子节点可以选也可以不选;
dp[x][1]+=dp[t][0];//x选的时候 子节点一定不能选
}
}
int main()
{
while(cin>>n)
{
if(n==0)
break;
init();
int ct=1;
string boss;
cin>>boss;
s.insert(boss);
mp[boss]=ct++;
for(int i=0;i<n-1;i++)
{
string a,b;
cin>>a>>b;
if(s.count(a)==0)
{
mp[a]=ct++;
//cout<<a<<" :"<<mp[a]<<endl;
s.insert(a);
}
if(s.count(b)==0)
{
mp[b]=ct++;
// cout<<b<<" :"<<mp[b]<<endl;
s.insert(b);
}
add(mp[b],mp[a]);
}
dfs(1);
cout<<max(dp[1][0],dp[1][1])<<" ";
int flag=1;
for(int i=1;i<=n;i++)
{
if(dp[i][0]>=dp[i][1])
{
for(int j=head[i];j!=-1;j=side[j].next)
{
int t=side[j].to;
if(dp[t][0]==dp[t][1])
{
flag=0;
break;
}
}
}
if(flag==0)
break;
}
if(dp[1][0]==dp[1][1])
flag=0;
if(flag==1)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}