hiho challenge 15 C题

DP题。状态很容易设,dp[u][0]表示u点子树解决,dp[u][1]表示剩一条链,dp[u][2]表示邻边全炸.

转移有点难,看代码解释:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

const int MAX=100005;

vector<int>t[MAX];
int dp[MAX][3];


void dfs(int u,int f){
	dp[u][0]=dp[u][1]=dp[u][2]=0;
	int sz=t[u].size(),v,counts=0,s=0;
	for(int i=0;i<sz;i++){
		v=t[u][i];
		if(v!=f){
			dfs(v,u);
		}
		else continue;
		s+=min(min(dp[v][0],dp[v][1]),dp[v][2]);
		if(dp[v][2]>min(dp[v][0],dp[v][1])){////dp[v][2]>min(dp[v][0],dp[v][1])这种情况表示选择不是全炸的,否则全炸,因为全炸的话,就不需要再多一次操作了。除非不是
		///全炸的更少,因为如果更少,则dp[v][2]至少大1
			counts++;///统计有多少个是儿子节点不是子邻边全炸的,这样每两个儿子节点就可以使用一次解决。
		}
		dp[u][2]+=dp[v][0];///邻边全炸,肯定是要儿子节点的子树全部解决。
	}
	dp[u][0]+=s+counts/2+((counts%2)?1:0);///如果有奇数个儿子节点不是邻边全炸,就/2后,需要一次和根节点的操作才可以全解决,偶数则不需要
	dp[u][1]+=s+counts/2;////剩一条链也不需要加上一次操作
	dp[u][2]++;
}

int main(){
	int n,u,v;
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;i++) t[i].clear();
		for(int i=1;i<n;i++){
			scanf("%d%d",&u,&v);
			t[u].push_back(v);
			t[v].push_back(u);
		}
		dfs(1,0);
		printf("%d
",min(dp[1][2],dp[1][0]));
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4886405.html