今天我们来做有关换根的树形动态规划问题,解决这类问题首先必须明白换根的基本思想,理解将子节点作为根之后哪些节点的深度变大,哪些节点的深度变小了。
同时做这类问题,要时常与贪心思想相结合理解,找出最大深度与次大深度,这常常是解决路径长度问题的关键。
小蓝和小桥是两位花园爱好者,她们在自己的花园里种了一棵 n个节点的树,每条边的长度为 k。初始时,根节点为 1 号节点。她们想把这棵树卖掉,但是想卖个好价钱。树的价值被定义为根节点到所有节点的路径长度的最大值。
为了让这棵树更有价值,小蓝和小桥可以对这棵树进行一种操作:花费 c的代价,将根节点从当前的节点移动到它的一个相邻节点上。注意,这个操作不会改变树的形态,只是改变了根节点的位置。
她们希望通过尽可能地进行操作,使得卖出去的这棵树的盈利最大。盈利被定义为卖出去的树的价值减去操作的总代价。
请你帮助她们,找出她们能够获得的最大盈利。
第一行包含一个整数 t,表示测试数据组数。
每组数据第一行包含三个整数 n、k 和 c,表示树的节点数、每条边的长度和进行一次移动操作的代价。
接下来 n−1 行,每行描述树的一条边,包含两个整数 ui 和 vi,表示树中连接 ui 和 vi 之间的一条边。
对于每组数据,输出一个整数,表示最大盈利。
#include
using namespace std;
const int N=1e5+3;
using ll=long long;
ll n,k,c;
ll dep1[N],dep2[N],dep[N];
vectorg[N];
void dfs1(int x,int fa){
for(const auto y:g[x]){
if(y==fa)continue;
dep1[y]=dep1[x]+1;
dfs1(y,x);
}
}
int main()
{
int t;
cin>>t;
while(t--){
cin>>n>>k>>c;
memset(dep1,0,sizeof(dep1));
memset(dep2,0,sizeof(dep2));
for(int i=1;i<=n;i++)g[i].clear();
for(int i=1;i>u>>v;
g[u].push_back(v),g[v].push_back(u);
}
dfs1(1,0);
ll ans=0;
int pos=1;
for(int i=1;i<=n;i++){
dep2[i]=dep1[i];
if(dep1[i]>dep1[pos])pos=i;
ans=max(ans,dep1[i]*k);
}
memset(dep1,0,sizeof(dep1));
dfs1(pos,0);
for(int i=1;i<=n;i++){
ans=max(ans,dep1[i]*k-dep2[i]*c);
}
cout<
思路:这道题就是典型的树形DP+贪心的结合思想,首先要计算当前节点到子节点的最大路径,然后通过查找离根节点最远的节点作为第二个根节点,进行换根操作,第二次查找。容易出错的地方在于由于有测试次数,要注意对原先容器清空操作,尤其是进行换根遍历的时候。
2.问题描述
小明在游戏中参加了一个帮派,这一天他突然想知道自己在帮派中是什么地位,但是帮派的查询系统突然坏了,目前只能知道每个人的附属关系,请问你能帮帮他重建关系网并找出他的地位吗?
给定一个正整数 n ,代表该帮派的总人数,并且小明的序号是 m,给出这 n个人中每个人的附属关系,确保给出的关系网为一棵树。帮派地位的定义是按照自己手下有多少帮众决定的,注意手下的手下也算是自己的手下。如果手下的帮众相同则按序号较小的在前面。你能帮助小明找到自己的帮派地位吗?
第一行,两个正整数 n (1≤n≤105) 和 m(1≤m≤n) ,代表该帮派的总人数以及小明的序号。
接下来 n−1行,每行两个正整数,格式如下:
l r
(1≤l,r≤n), 代表序号为 l的人附属于序号为 r 的人。一行,包含 1 个正整数,输出按手下人数多少排序后小明的排名。
#include
using namespace std;
const int N=1e5+3;
int cnt,a[N];
bool rot[N];
vectorg[N];
void dfs(int x,int fa){
a[x]=++cnt;
for(const auto y:g[x]){
dfs(y,x);
}
a[x]=cnt-a[x];
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i>l>>r;
g[r].push_back(l);
rot[r]=1;
}
int root=1;
for(int i=1;i<=n;i++){
if(rot[i]){
root=i;
break;
}
}
dfs(root,0);
int ans=1;
for(int i=1;i<=n;i++){
if(i=a[m])ans++;
}else{
if(a[i]>a[m])ans++;
}
}
cout<
这道题的关键是dfs中求解每个节点所拥有的子节点个数,同时,从拥有子节点的节点开始向下遍历,因为题目并没有说1就是根节点,这些都可以当作模版去牢记。
今天的分享就到这里,主要是第一题关于换根的操作,各位道友可以一起学习思考一下,有疑问的地方可以在评论区告诉博主哦。最后麻烦大家多多关注!