动态规划-树形DP(换根)

今天我们来做有关换根的树形动态规划问题,解决这类问题首先必须明白换根的基本思想,理解将子节点作为根之后哪些节点的深度变大,哪些节点的深度变小了。

同时做这类问题,要时常与贪心思想相结合理解,找出最大深度与次大深度,这常常是解决路径长度问题的关键。

1.问题描述

小蓝和小桥是两位花园爱好者,她们在自己的花园里种了一棵 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就是根节点,这些都可以当作模版去牢记。

今天的分享就到这里,主要是第一题关于换根的操作,各位道友可以一起学习思考一下,有疑问的地方可以在评论区告诉博主哦。最后麻烦大家多多关注!

你可能感兴趣的:(动态规划-树形DP(换根))