UVa - 1220 - Party at Hali-Bula ( 树形DP 求最大独立集 )



题目大意:有1个大boss,n-1个员工,每个员工只有一个直接上司,一个领导可以有多个员工,现在举行一个聚会,有直接关系的人不会同时出现,问最多能请多少个人,情况是否唯一。

题目思路:树形动态规划,求最大独立集,

1.设 dp[i][0]dp[i][1] 分别不表示选择和选择 i 结点所能得到的最大独立集人数。

状态转移方程:

dp[ i ][ 0 ] = sum(dp[ j ][ 0 ] , dp[ j ][ 1 ]); ( j 是 i 的孩子结点 )

dp[ i ][ 1 ] = sum(dp[ j ][ 0 ]) ; ( j 是 i 的孩子结点 )

2.设 f[i][0] f[i][1] 分别表示不选择和选择则 i 结点的唯一性,1表示不唯一 ,0 表示唯一

求法:

在求dp[ i ][ 0 ] = sum(dp[ j ][ 0 ] , dp[ j ][ 1 ]); ( j 是 i 的孩子结点 )时:

即:求不选择 i 结点的情况:(选择 j 或者不选都可以 ) 当 dp[ j ][ 0 ]大的时候,即不选择 j 结点,如果不选择 j 结点的情况不是唯一的,那么不选择 i 结点的情况也不是唯一的。同理dp[ j ][ 1 ]比较大的时候道理是一样的。

故有

		if(dp[son][0] > dp[son][1]){
			dp[u][0] += dp[son][0];
			if(f[son][0]) f[u][0] = 1;
		}
		else if(dp[son][0] == dp[son][1]){ 
			dp[u][0]+=dp[son][0];
			f[u][0] = 1;
		}
		else {
			dp[u][0] += dp[son][1];
			if(f[son][1]) f[u][0] =1;
		}




#include<cstdio>	
#include<iostream>	
#include<vector>
#include<string>
#include<cstring>
#include<map>
#define N 205
using namespace std;

vector<int> sons[N]; //存放孩子结点 
map<string,int> v; //读取数据 
int dp[N][2]; //dp[i][1]:选择i结点的最大独立集 dp[i][0]:不选择i结点的最大独立集 
int f[N][2];  //f[i][1] : 选择i结点的最大独立集是否唯一(1:不唯一 0:唯一) f[i][0] : 

void dfs(int u){
	if(sons[u].empty()){ //叶子结点 
		dp[u][0]=0; 
		dp[u][1]=1;
		return ;
	}
	
	int size = sons[u].size();
	
	for(int i=0 ;i<size ;i++){
		int son = sons[u][i];
		dfs(son);
		//计算选择u结点 
		dp[u][1] += dp[son][0];
		if(f[son][0]) f[u][1] = 1;
		// 计算不选择u结点 
		if(dp[son][0] > dp[son][1]){
			dp[u][0] += dp[son][0];
			if(f[son][0]) f[u][0] = 1;
		}
		else if(dp[son][0] == dp[son][1]){ 
			dp[u][0]+=dp[son][0];
			f[u][0] = 1;
		}
		else {
			dp[u][0] += dp[son][1];
			if(f[son][1]) f[u][0] =1;
		}
	}
	dp[u][1]++; //加上u结点本身 
}
int n;

int main(){
	while(scanf("%d",&n)&&n){
		memset(dp,0,sizeof(dp));
		memset(f,0,sizeof(f));
		int top = 1;
		string s1,s2;
		cin>>s1;
		v[s1] = top++;
		for(int i=1 ;i<n ;i++){
			cin>>s1>>s2;
			if(!v[s1]) v[s1] = top++;
			if(!v[s2]) v[s2] = top++;
			sons[v[s2]].push_back(v[s1]); 
		}
		dfs(1);
		
		if(dp[1][1]==dp[1][0]){
			printf("%d No\n",dp[1][1]);
		} 
		else if(dp[1][1]>dp[1][0]){
			printf("%d %s\n",dp[1][1],f[1][1] ? "No" : "Yes");
		}
		else{
			printf("%d %s\n",dp[1][0],f[1][0] ? "No" : "Yes");
		}
		
		v.clear();
		for(int i=1 ;i<=n ;i++){
			sons[i].clear();
		}
	}	
	return 0;
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值