2016夏季练习——dp

来源:POJ2378

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN=10010;
vector<int>tree[MAXN];
bool vis[MAXN];
int dp[MAXN];
int n;
int fa,so;
//题意要求就是我们需要将这个图拆开,使得每一个联通分量都是n/2
//原问题就是这个
//子问题就是我们在一个子图上拆开,是的我的每一个的联通分量小于n/2
//所以我们只要判断其子树满足一个条件即可
//我们贪心的来看这个问题:
//我们删除最少点,所以对于某一棵节点,如果它的有一棵子树的节点大于n/2
//那么这个点显然不是首选点
//因为这个点除此以外的所有子树的和也不过n/2-1(注意其本身)
//此时删去这个点完全没有意义
//从而我们dp计算一个值:一个节点的最多节点子树的度
//我们用dp[rt]来记录rt所有的子树中上述的情况,所以只一次dfs就好
//实际上这个题目和dp关系不大
int dfs(int rt,int fa){
	int maxn=0,ans=0;
	//if(dp[rt])return dp[rt];
	dp[rt]=0;
	int sum=1;
	for(int i=0;i<tree[rt].size();i++){
		if(tree[rt][i]==fa) continue;
		maxn=dfs(tree[rt][i],rt);
		ans=max(ans,maxn);
		sum+=maxn;
	}
	dp[rt]=max(ans,n-sum);//这个地方需要理解一下,画个图就好
	return sum;
}
int main(){
	while(scanf("%d",&n)!=EOF){
		for(int i=0;i<n;i++) tree[i].clear();
		memset(dp,0,sizeof(dp));
		memset(vis,0,sizeof(vis));
		for(int i=0;i<n-1;i++){
			scanf("%d%d",&fa,&so);
			tree[fa].push_back(so);
			tree[so].push_back(fa);
		}
		dfs(1,0);
		bool flag=0;
		/*for(int i=1;i<=n;i++)
			cout<<dp[i]<<" ";
		cout<<endl;*/
		for(int i=1;i<=n;i++){
			if(dp[i]<=n/2){
				flag=1;
				cout<<i<<endl;
			}
		}
		if(!flag) cout<<"NO"<<endl;
	}
	return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值