3个经典树上问题

1.树的最大独立集

任选一个点作为根,有了以下得状态

 f[i][0/1] 

f[i][0] += max(f[y][0], f[y][1]

f[i][1] += f[i][0]

#include <iostream>
#include <vector>
using namespace std; 
 const int N=6005;
 vector<vector<int> >g(N);
 int n,vis[N],f[N][2];
 
 void dp(int x){
 	for(int i=0;i<g[x].size();i++){
 		int y=g[x][i];
 		dp(y);
 		f[x][0]+=max(f[y][0],f[y][1]);
 		f[x][1]+=f[y][0];
	 }
 }
 int main(){ 
    int i,x,y,rt=1;
    cin>>n;
    for(i=1;i<=n;i++) cin>>f[i][1];
    
    for(i=1;i<n;i++){
    	cin>>x>>y,g[y].push_back(x);
    	vis[x]=1;
	}
	while(vis[rt]) rt++;
	dp(rt);
	cout<<max(f[rt][0],f[rt][1]);
 }
 
 
 

 

2. 树的重心(找一个点, 使得以该点作为根节点时, size(son(x)) 的最大值最小

 同样任选一个点作为根

 答案= max{   max{ size(son[y] ) }  ,  n-size(x) } ,        y是x的儿子

#include <iostream>
#include <vector>
using namespace std ;
 const int N=100;
 vector<vector<int> > g(N);
 int sz[N],f[N],n;
 void dfs(int x){
 	sz[x]=1;
 	int mx=0;
 	for(int i=0;i<g[x].size();i++){
 		int y=g[x][i];
 		dfs(y);
 		sz[x]+=sz[y];
 		mx=max(mx,sz[y]);
	} 
	f[x]=max(n-sz[x],mx);
 }
 int main(){
 	int i,x,y,ans=1<<30;
 	cin>>n;
 	for(i=1;i<n;i++){
    	cin>>x>>y,g[x].push_back(y);
	}
	dfs(1);
	for(i=1;i<=n;i++){
		if(f[i]<ans) ans=f[i],x=i;
	}
	printf("%d %d\n",x,ans);
 }
 

3.树上最长路径

形状应该为两条路径集于一个根节点

 记录 d1[x] , d2[x] ,x子树的最长路径最大值和次大值

ans = d1[x]+d2[x] 


#include <iostream>
#include <vector> 
#include <cstring>
using namespace std ;
const int N=1e5;
 vector<vector<int> >g(N);
 int n,d1[N],d2[N];

 void dp(int x,int fa){
    d1[x]=d2[x]=0;

    for(int i=0;i<g[x].size();i++){
        int y=g[x][i]; if(fa==y) continue;
        dp(y,x);

        d1[x]=max(d1[x],d1[y]+1);
    }
    for(int i=0;i<g[x].size();i++){
        int y=g[x][i]; if(fa==y) continue;

        if(d1[x]!=1+d1[y]) d2[x]=max(d2[x],d1[y]);
    }
 }
 int main(){
    int ans=0,x,y,i;
    cin>>n;
    for(i=1;i<n;i++) 
	  cin>>x>>y,g[x].push_back(y),g[y].push_back(x);;

    dp(1,0);
    for(i=1;i<=n;i++) ans=max(ans,d1[i]+d2[i]+2);
    cout<<ans;
 }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值