1. 程式人生 > >UVa 1218 樹形DP

UVa 1218 樹形DP

思路主要是紫書 上的,我在這裡說一下建樹的細節。

#include <vector>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
vector<int>  son[10005];
int dp[10005][3];
int vis[10005];
//int have[10005];
const int INF = 1<<29;

void search(int u)
{
	vis[u] = 1;
	dp[u][0] = 0;
	dp[u][1] = 0;
	dp[u][2] = 1;
	if(son[u].empty())
	{
		dp[u][0] = INF;
		return ;
	}
	for(int i = 0; i < son[u].size(); i++)
	{
		if(vis[son[u][i]]) continue;                           //從樹根開始向下遍歷,一旦這個節點用過了,就不能再用了,否則就會出現遍歷一個節點的兒子時遍歷到這個節點的父親的情況	      
		if(!vis[son[u][i]])  search(son[u][i]);
		dp[u][1] += dp[son[u][i]][0];
		dp[u][2] += min(dp[son[u][i]][2], dp[son[u][i]][1]);
	}
	int sum = 0;
	for(int i = 0; i < son[u].size(); i++)
		sum += dp[son[u][i]][0];
	dp[u][0] = sum + dp[son[u][0]][2] - dp[son[u][0]][0];	 
	for(int i = 0; i < son[u].size(); i++)
		dp[u][0] = min(dp[u][0], sum + dp[son[u][i]][2] - dp[son[u][i]][0]);
	return ;
}
		
int main()
{
	//freopen("ztest.txt","r",stdin);
	//freopen("zans.txt","w",stdout);
	int n, root;
	int flagg = 1;
	while(scanf("%d",&n))
	{
		for(int i = 0; i <= n; i++)
			son[i].clear();
		int flag = 1, temp1, temp2;
		memset(vis, 0, sizeof(vis));
		//memset(have, 0, sizeof(have));
		while(scanf("%d",&temp1) && temp1)
		{
			if(flag == 1)
			{
				root = temp1;
				flag = 0;
			}
			if(temp1 == -1)  {flagg = 0; break;}
			scanf("%d",&temp2);
			son[temp1].push_back(temp2);                //這時建立的樹中,兒子的兒子中有父親,vis[]解決了這個問題
			son[temp2].push_back(temp1);                
		}
		search(root);
		//for(int i = 1; i <= n; i++)
		//	printf("%d  %d  %d\n",dp[i][0],dp[i][1],dp[i][2]);
		printf("%d\n",min(dp[root][2], dp[root][0]));
		if(!flagg) break;
	}
	return 0;
}