树形dp_2021-06-01

树的中心


树 形 d p , 换 根 d p , 必 会 题 目 , 超 级 经 典 , 奈 何 当 初 学 的 时 候 理 解 的 不 深 入 , 还 得 重 新 理 解 树形dp,换根dp,必会题目,超级经典,奈何当初学的时候理解的不深入,还得重新理解 dp,dp,


前 置 知 识 : 树 形 d p 求 树 的 直 径 , 换 根 d p 换 根 d p 可 以 把 O ( N 2 ) 的 做 法 降 低 到 O ( N ) 的 时 间 复 杂 度 , 力 推 ! 前置知识:树形dp求树的直径,换根dp\\ 换根dp可以把O(N^2)的做法降低到O(N)的时间复杂度,力推! dpdpdpO(N2)O(N)


给 定 一 根 无 根 树 , 在 树 中 找 到 一 个 点 , 使 得 该 点 到 树 中 其 他 节 点 的 最 远 距 离 最 近 给定一根无根树,在树中找到一个点,使得该点到树中其他节点的最远距离最近 使

在这里插入图片描述


熟练到背过
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=1e4+10,M=1e9+7;
ll n,m,_;

int h[N],e[N*2],ne[N*2],w[N*2],idx;

void add(int a,int b,int c)
{
	e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

int dis[N],predis[N];//存储从u点向下走的最长和次长路径
int nex[N];//存储从u节点向下的最长路径的下一个点是谁?
int up[N];
int dfs(int u,int fa)
{
	// if(h[u]==-1)
	// {
		// puts("---");
		// return 0;//子节点,这句话永远也用不到,是假的
	// }
	
	dis[u]=predis[u]=-0x3f3f3f3f;//初始化为负无穷
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		int d=dfs(j,u)+w[i];
		if(d>=dis[u])
		{
			predis[u]=dis[u];dis[u]=d;
			nex[u]=j;
		}
		else if(d>predis[u])
		{
			predis[u]=d;
		}
		
	}
	
	if(dis[u]==-0x3f3f3f3f)//判断子节点
	{
		dis[u]=predis[u]=0;
	}
		
	return dis[u];
}

int ans=0x3f3f3f3f;

void dfs_up(int u,int fa)
{
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		
		if(nex[u]==j)//已知父节点,更新子节点
			up[j]=max(up[u],predis[u])+w[i];
		else
			up[j]=max(up[u],dis[u])+w[i];
		
		dfs_up(j,u);
	}
}

void solve()
{
	cin>>n;
	mem(h,-1);
	for(int i=0;i<n-1;i++)
	{
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c),add(b,a,c);
	}
	dfs(1,-1);
	
	dfs_up(1,-1);
	
	for(int i=1;i<=n;i++)
	{
		// cout<<i<<" "<<dis[i]<<" "<<up[i]<<endl;
		ans=min(ans,max(dis[i],up[i]));
	}
	
	cout<<ans<<endl;
}

int main()
{
	solve();
	return 0;
}

特别离谱的一件事

请找出下边两端代码的不同,服气了
void dfs_up(int u,int fa)
{
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		
		if(nex[u]==j)//已知父节点,更新子节点
			up[j]=max(up[u],predis[u])+w[i];
		else
			up[j]=max(up[u],dis[u])+w[i];
		
		dfs(j,u);
	}
}

void dfs_up(int u, int fa)
{
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j == fa) continue;

        if (nex[u] == j) up[j] = max(up[u], predis[u]) + w[i];
        else up[j] = max(up[u], dis[u]) + w[i];

        dfs_up(j, u);
    }
}

换根dp推荐题目:AcWing 287. 积蓄程度

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define pb push_back 
#define in insert
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=(a);i<=(n);++i)
#define fo_(i,a,n) for(int i=(a);i<(n);++i)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;

template<typename T>
ostream& operator<<(ostream& os,const vector<T>&v){for(int i=0,j=0;i<v.size();i++,j++)if(j>=5){j=0;puts("");}else os<<v[i]<<" ";return os;}
template<typename T>
ostream& operator<<(ostream& os,const set<T>&v){for(auto c:v)os<<c<<" ";return os;}

typedef pair<int,int>PII;
typedef pair<long,long>PLL;

typedef long long ll;
typedef unsigned long long ull; 
const int N=2e5+10,M=1e9+7;
ll n,m,_;

int h[N],e[N*2],ne[N*2],idx,c[N*2];

void add(int a,int b,int w)
{
	e[idx]=b,ne[idx]=h[a],c[idx]=w,h[a]=idx++;
}

int dp[N];
int f[N];
int deg[N];
int ans;
void dfs(int u,int fa)
{
    dp[u]=0;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		
		dfs(j,u);
		
		if(deg[j]==1)
			dp[u]+=c[i];
		else
			dp[u]+=min(dp[j],c[i]);
	}
}

void dfs_up(int u,int fa)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==fa)continue;
        
        if(deg[j]==1)
            f[j]=dp[j]+c[i];
        else
        f[j]=dp[j]+min(c[i],f[u]-min(dp[j],c[i]));
        
        dfs_up(j,u);//又犯了同样的错误了
    }
}

void solve()
{
	mem(h,-1);
	idx=0;
    ans=0;
    mem(deg,0);
	cin>>n;
	fo(i,0,n-2){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w),add(v,u,w);
		deg[u]++,deg[v]++;
	}	
	
	int root=1;
	while(root<=n&&deg[root]==1)root++;
	
	if(root>n)//只有远点和汇点,也就是所有点的度数全是1
	{
	    cout<<c[0]<<endl;
	    return ;
	}
	dfs(root,-1);
	f[root]=dp[root];
	dfs_up(root,-1);
	
	for(int i=1;i<=n;i++)
	{
	   // cout<<i<<" "<<dp[i]<<" "<<f[i]<<endl;
	    ans=max(ans,f[i]);
	}
	cout<<ans<<endl;
}

int main()
{
	cin>>_;
	while(_--)
	{
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值