POJ 3342 Party at Hali-Bula

20 篇文章 0 订阅

Party at Hali-Bula

好久没有写解题报告了,这次把上个月做的一些题都写写,上个月做了不少的树形DP,先从这个比较简单的入手。

这个题目hdu 1520 Anniversary party那道很裸的树形DP差不多,只不过这道题不仅要求出最多的人,还要判断是不是唯一。主要是如何判断人数是不是唯一的。出现多解的情况可能有如下两种,如果对于根节点而言,选与不选的解是相同的,则说明这样肯定会是多解,另一种情况就是由于儿子节点的多解导致父亲节点的多解。所以我们可以用一个二维数组来标记某个节点由儿子节点更新到父亲节点的时候是不是有多解。对于选择父亲节点,那么可能导致的情况就是儿子节点不选的时候有多解,那么需要将这种多解的标记更新到父亲节点。对于不选择父亲节点,那么就有可能是儿子节点选与不选解都是相同的,或者儿子节点较大的那种选择有多解标记,此时我们标记父亲节点。具体代码如下:

/*
 *author    : csuchenan
 *PROG      : POJ3342
 *Algorithm : TreeDP ,注意判重复的情况,
 *这里如果某个点出现重复的情况那么就更新给父亲节点
 *对于根节点只需要特判一下
 *10792694	csuchenan	3342	Accepted	212K	0MS	C++	1955B	2012-09-11 11:12:53
*/
#include <cstdio>
#include <cstring>
#include <vector>
using std::vector ;
#define maxn 205

vector<int> G[maxn] ;
char name[205][105] ;
int  dp[maxn][2] ;
bool special[maxn][2] ;

int  n ;
int  len ;

int insert(char * str){
	strcpy(name[len] , str ) ;
	len ++ ;
	return len - 1 ;
}

int find(char *str){
	int i(0) ;
	while(i < len && strcmp(str , name[i]) != 0 )
		i ++ ;
	if(i == len )
		return -1 ;
	return i ;
}

void init(){
	for(int i = 0 ; i <= n ; i ++){
		G[i].clear() ;
	}
	memset(name , 0 , sizeof(name)) ;
	memset(dp , 0 , sizeof(dp)) ;
	memset(special , 0x1 , sizeof(special)) ;
	len = 0 ;
}

inline int max(int x , int y){
	return x > y ? x : y ;
}

void dfs(int v){
	for(vector<int> :: size_type i = 0 ; i != G[v].size() ; i ++){
		int u = G[v][i] ;
		dfs(u ) ;
		dp[v][0] += max(dp[u][1] , dp[u][0]) ;
		dp[v][1] += dp[u][0] ;

		if(dp[u][1] == dp[u][0]){
			special[v][0] = 0 ;
		}
		else if(dp[u][0] > dp[u][1] && special[v][0]){
			special[v][0] = special[u][0] ;
		}
		else if(dp[u][0]< dp[u][1] && special[v][0]){
			special[v][0] = special[u][1] ;
		}
		if(special[v][1]){
            special[v][1] = special[u][0] ;
		}
	}
	dp[v][1] ++ ;
}

int main(){
	char str[105] ;
        //freopen("POJ3342.txt" , "r" , stdin) ;
	while(scanf("%d" , &n) , n){
		init() ;
		scanf("%s" , str) ;
		insert(str) ;
		int p , q ;

		for(int i = 1 ; i < n ; i ++){
			scanf("%s" , str) ;
			if( (p=find(str)) == -1 )
				p = insert(str) ;
			scanf("%s" , str) ;
			if( (q=find(str)) == -1 )
				q = insert(str) ;

 			//G[p].push_back(q) ;
 			G[q].push_back(p) ;
  		}
  		dfs(0) ;
  		int ans = max(dp[0][0] , dp[0][1]) ;
		printf("%d " , ans ) ;
		if(dp[0][0] == dp[0][1] )
			puts("No") ;
		else if( ((dp[0][0] > dp[0][1]) && !special[0][0]) ||
				((dp[0][1] > dp[0][0]) && !special[0][1] ) )
			puts("No") ;
		else
			puts("Yes") ;
	}
	return 0 ;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值