当前位置: 首页 > news >正文

最简单的做网站的工具中天建设集团有限公司排名

最简单的做网站的工具,中天建设集团有限公司排名,抖音推广网站,微信拼团小程序怎么做基本概念: 换根dp是树上dp的一种 我们在什么时候需要用到换根dp呢? 当题目询问的属性,是需要当前结点为根时的属性,这个时候,我们就要使用换根dp 换根dp的基本思路: 假设题目询问的的属性为x 通常我们…

基本概念:

换根dp是树上dp的一种

我们在什么时候需要用到换根dp呢?

当题目询问的属性,是需要当前结点为根时的属性,这个时候,我们就要使用换根dp

换根dp的基本思路:

假设题目询问的的属性为x

通常我们会进行两次dfs

第一次dfs,我们选取任意一个结点作为给出的无根树的根,对其进行dfs,并求出这个根的x,以及一些其他辅助数组(即节点与其子树的一些属性关系)

第二次dfs,我们记dp[i]为对于结点i而言,节点i作为树的根时,我们要求的属性x

那么,令当前结点为u,其子节点为v,我们需要对推到出dp[u]转移到dp[v]的转移方程,从而成功求出结点v作为根时的属性x,如此递归下去,直到将所有结点的dp值都求出来,我们就可以求得题目的询问答案了

例题1:

题目链接:P3478 [POI2008] STA-Station - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目大意:

给出一棵有n个节点的树,求出这样的一个节点,使得以这个节点为根时,所有节点的深度之和最大

题目分析:由于题目很直白地讲了是要求以节点为根时的属性,那么当然是采用换根dp的方法

由于是深度之间的转移,我们设数组s[i],表示的是以节点i为根的子树的深度之和

我们先选取1号节点为根,进行第一次dfs,预处理出s数组的值以及dp[1]的值

之后我们需要用第二次dfs进行关系转移

我们考察当前节点u以及其子结点v

当根从u转移到v时,以v为根的子树的每个结点的深度都减小了1,也就是总深度减小了s[v]

而对于以u为根的,且排除了v结点的子树而言,他们每个结点的深度都增大了1,也就是总深度增大了n-s[v]

由此,我们可以推到出,当根由u转到v时,总深度从dp[u]变到dp[v]的变化是:增加了n-2*s[v]

即得转移方程:dp[v]=dp[u]+n-2*s[v]

至此,我们只需要用第二次dfs求出每个节点的dp值,最后再从1到n遍历出最大的dp值,其对应的节点编号即为询问答案

实现代码:

#include <cstdio>
#include <iostream>
using namespace std;
// 换根dp
// 用s[i]表示以i为根时其子树的结点个数
// 令u为当前结点
// v为当前结点的子结点
// 则有s[u]=s[v]+1
// 第一次dfs,选取任意一个根,来预处理出所有的s[i]//换根的状态转移:
//dp[i]表示以i为根时,所有结点的深度之和
//令当前根为u,v为其子结点
//则将根由u转到v时
//所有在v的子树上的结点深度都会减少1,那么总深度就减少了s[v]
//所有不在v的子树上的结点的深度都会增加1,那么总深度就增加了n-s[v]
//由此,我们得知,dp[v]=dp[u]+n-2*s[v],此为状态转移方程
//我们第二次dfs来进行这个操作//最后只需遍历dp[i],找到最大的即可
const int N=1E6+10,M=N<<1;
int n;
//链式前向星加边
int to[M],nxt[M],h[N],tot;
void add(int a,int b){to[++tot]=b;nxt[tot]=h[a];h[a]=tot;
}
unsigned long long s[N],dp[N],dep[N];
void dfs1(int f,int u){dep[u]=dep[f]+1;s[u]++;for(int i=h[u],v;v=to[i];i=nxt[i]){if(v==f) continue;dfs1(u,v);s[u]+=s[v];}
}
void dfs2(int f,int u){for(int i=h[u],v;v=to[i];i=nxt[i]){if(v==f) continue;dp[v]=dp[u]+n-2*s[v];dfs2(u,v);}
}
int main(){cin>>n;for(int i=1,a,b;i<n;i++){cin>>a>>b;add(a,b);add(b,a);}dep[0]=-1;dfs1(0,1);for(int i=1;i<=n;i++) dp[1]+=dep[i];dfs2(0,1);unsigned long long max=0;int ans;for(int i=1;i<=n;i++)if(dp[i]>max){max=dp[i];ans=i;}cout<<ans;return 0;
}

例题2:

题目链接:3585 -- 积累度 --- 3585 -- Accumulation Degree (poj.org)

题目大意:定义A[i],表示的是结点i所能流到所有叶子结点的最大流量之和。其中由一个结点流向另一个节点的流量不能大于连接这两个结点的边的权值。

题目分析:

由于流量是由父结点流向子结点,这使得dfs不好处理(不知道一开始要流多少,有可能等于边权,也有可能小于边权)

所以我们选择把流量从子结点向父结点转移,也就是说,我们认为一个子结点v需要的流量为x,那么回去找它的父结点u,如果连接u与v的边权w大于等于x,那么父结点u所需要的流量就增加x;否则,父结点所需要的流量只能增加w(因为它最多只能流w到子结点v上)

由此,我们设出数组s1[i],表示的是i结点所需要的流量大小,由上述可以写出s1数组的转移方程,令当前结点为u,其子结点为v,那么s1[u]=s1[v]>w?w:s1[v]

又由于叶子结点v没有子结点了,那么它所需要的流量就应该初始化为连结到v的边的权值,因为它最多就只能要那么多。

怎么找到叶子结点呢?只要注意到其入度为1就好了

这样,我们就可以选取一个结点作为根进行第一次dfs,预处理出s1数组

这里注意不能选取叶子结点作为第一次dfs的根,因为会丢失掉其s1的值

第二次dfs:

设dp[u],其意义就是A[u],那么我们就需要推导其转移方程

考察当前根结点u转移到其子节点v

dp[u]表示u结点所需要的流量,那么这部分流量可以被分为两部分

一部分是v结点所转移到u的流量,我们记为Q1

另一部分是除了v节点外,其他子节点所需要的流量,我们记为Q2,则有Q2=dp[u]-Q1

考察v结点转移到u的流量:

当v结点所需的流量s1[v]>=w时,(w为边权),v只能转移w给u,此时Q1=w

而当s1[v]<w时,v能转移s1[v]给u,此时Q1=s1[v]

当根由u转到v时,Q2这部分流量就需要转到v上,由于边权w的限制,Q2的转移量可能会发生变化,我们设其转移量为Q2'

当Q2>=w时,只能转移w给v,此时Q2'=w

而当Q2<w时,就转移Q2给v,此时Q2'=dp[u]-Q1

而对于节点v而言,当其成为根后,其所需的流量,一部分来自于Q2的转移,另一部分来自于结点v自身所需的流量s1[v],也就是说,dp[v]=Q2'+s1[v]

至此,我们成功推导出了状态转移方程,只需进行第二次dfs,即可求出所有的dp值,进而求出最大值

细节问题:当v为叶子结点时,由于其所需的s1[v]来自于一开始的初始化,当它成为根后,其实并不需要s1[v]这一部分的贡献,因此,我们要将这个值减去

实现代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 2E5 + 10, M = N << 1;
int to[M], nxt[M], w[M], h[N], tot;//链式前向星
int in[N];//记录入度
int n, T;
//加边
void add(int a, int b, int c) {to[++tot] = b;w[tot] = c;nxt[tot] = h[a];h[a] = tot;in[b]++;
}long long s1[N], dp[N];
//第一次dfs获取s1数组
void dfs1(int f, int u) {for (int i = h[u], v; v = to[i]; i = nxt[i]) {if (f == v) continue;//当v为叶子结点时,dfs其之前,先初始化s1[v]的值为边权if (in[v] == 1) s1[v] = w[i];dfs1(u, v);//dfs完后,子结点向父结点转移s1的值s1[u] += s1[v] <= w[i] ? s1[v] : w[i];}
}
//第二次dfs
void dfs2(int f, int u) {for (int i = h[u], v; v = to[i]; i = nxt[i]) {if (f == v) continue;//当Q1=w[i]时,此时Q2=dp[u]-w[i]if (s1[v] >= w[i]) {//当Q2<w[i]时,此时Q2'=dp[u]-w[i]if (dp[u] - w[i] < w[i]) dp[v] = dp[u] - w[i] + s1[v];//否则Q2'=w[i]else dp[v] = w[i] + s1[v];//叶子结点减去重复贡献if (in[v] == 1) dp[v] -= s1[v];}//当Q1=s1[v]时,此时Q2=dp[u]-s1[v]else { //当Q2<w[i]时,此时Q2'=Q2if (dp[u] - s1[v] < w[i]) dp[v] = dp[u];//否则Q2'=w[i]else dp[v] = w[i] + s1[v];//叶子结点减去重复贡献if (in[v] == 1) dp[v] -= s1[v];}dfs2(u, v);}
}
int main()
{cin >> T;while (T--){//多组数据输入一定要记得初始化for (int i = 1; i <= tot; i++) to[i] = nxt[i] = w[i] = 0;for (int i = 1; i <= n; i++) in[i] = h[i] = s1[i] = dp[i] = 0;tot = 0;cin >> n;for (int i = 1, a, b, c; i < n; i++) {scanf("%d%d%d", &a, &b, &c);add(a, b, c); add(b, a, c);}int root = 1;//找到非叶子结点作为第一次dfs的根for (int i = 1; i <= n; i++)if (in[i] != 1) {root = i; break;}dfs1(0, root);dp[root] = s1[root];dfs2(0, root);long long ans = 0;//找最大值for (int i = 1; i <= n; i++) ans = max(ans, dp[i]);cout << ans << endl;}return 0;
}

http://www.yayakq.cn/news/816278/

相关文章:

  • phpcms校园网站杭州网页设计招聘
  • 携程旅游网官方网站 做攻略海淀周边网站建设
  • 伊川网站建设全国网站排名
  • 东莞大岭山网站建设米课中有个内贸网站建设
  • app制作外包阳江企业网站排名优化
  • 免费做网站教程贵州最好的网站建设推广公司
  • idea做百度网站规划设计网址
  • 上海企业网站排名优化wordpress wp_options
  • 网站如何获取用户信任类似传奇的网页游戏
  • 网站用什么cms四种营销策略
  • 传媒免费网站建设小程序免费制作平台企业中心
  • 网站标签怎么做跳转页面手机旅游网站建设
  • jsp旅游网站开发关键技术农业局网站建设方案
  • flash工作室网站模板网站 毕业设计代做
  • 我有域名跟空间能教我做网站吗网站流量的作用
  • 梧州建设网站如何制作一个小程序
  • 如何建设一个不备案的网站徐州网站关键词
  • 单位网站建设要求建自己的网站
  • 鲜花网站源码wordpress的程序文件
  • 大连的网站制作公司网站网页制作图片素材
  • 中天建设集团有限公司总网站建筑网2016农村别墅图大全
  • 网站建设有用吗xxx网站建设规划书
  • 番禺建网站网站开发课设心得
  • 做电商网站需要会些什么条件wordpress版本对应的php版本号
  • 购买友情链接网站网站建设开发费会计分录
  • 上海建网站wordpress支持移动
  • 如何保护网站模板网站建设分金手指专业十
  • 电子商务网站建设规划论文培训类网站模板
  • 金华手机建站模板480元做网站
  • 海南做房地产网站的网络公司Fastcgi做网站