HDU 2196 - Computer (Tree DP)
http://blog.csdn.net/shuangde800/article/details/9732825
http://www.cnblogs.com/kuangbin/archive/2012/08/28/2659915.html
由于对于一个节点来说,可能得到的距离最大的值的路径来自他的子树,或者从他的父节点过来,所以用两次DFS。
第一次DFS求出所有节点在他的子树范围内到叶子节点距离的最大值和第二大的值,第二次DFS更新从父节点过来的情况就可以了。
因为如果只存最大值的话,判断一个点的从父节点过来的最大值,那么如果他的父节点存的最大值正好是从该点过来的,那么就失去了从父节点过来的状态,所以要记录最大的两个值。
http://blog.csdn.net/dgq8211/article/details/10041625
任选一点为根节点,先求出每个节点到子节点的最大距离和次大距离,然后更新每个子节点的最大,次大距离,就是把每个子节点当一次根节点,如果它在父节点的最大距离边上,就用次大距离+到父节点的距离与自己的最大次大距离比较
http://blog.163.com/zjut_nizhenyang/blog/static/16957002920112610444983
先以 1 作为根节点进行一次 dfs 遍历,遍历的时候把以 第 i 为根节点往子树方向可以走到的最远距离和次远距离给求出来,且这两个距离是不在同一个分支中的
然后我们进行第二次的从根节点开始dfs遍历,这时候我们要判断的是对于一个节点 i ,除了子树方向上可能有最远距离外,我通过父节点方向是否可以有更加远的距离
,而对于这个操作,我们只要访问父节点所能到达的最远距离,然后接上一段就行了,但是这里会产生一个问题——就是父节点的最远距离可能是会经过当前这个节点
的,因此我们要进行判断当前节点是否在父节点的最远距离的分支上
如果在的话,那么我们要换一个分支,且要是最远的,这个其实就是第一次dfs中求出的与最远路径不在同一分支上的次远路径,我们接上这段进行转移
如果不在的话,那么我们直接接上父节点的最远路径进行转移就行了
Problem Description
A school bought the first computer some time ago(so this computer's id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are anxious about slow functioning of the net and want to know the maximum distance Si for which i-th computer needs to send signal (i.e. length of cable to the most distant computer). You need to provide this information.
Hint: the example input is corresponding to this graph. And from the graph, you can see that the computer 4 is farthest one from 1, so S1 = 3. Computer 4 and 5 are the farthest ones from 2, so S2 = 2. Computer 5 is the farthest one from 3, so S3 = 3. we also get S4 = 4, S5 = 4.
Hint: the example input is corresponding to this graph. And from the graph, you can see that the computer 4 is farthest one from 1, so S1 = 3. Computer 4 and 5 are the farthest ones from 2, so S2 = 2. Computer 5 is the farthest one from 3, so S3 = 3. we also get S4 = 4, S5 = 4.
Input
Input file contains multiple test cases.In each case there is natural number N (N<=10000) in the first line, followed by (N-1) lines with descriptions of computers. i-th line contains two natural numbers - number of computer, to which i-th computer is connected and length of cable used for connection. Total length of cable does not exceed 10^9. Numbers in lines of input are separated by a space.
Output
For each case output N lines. i-th line must contain number Si for i-th computer (1<=i<=N).
Sample Input
5 1 1 2 1 3 1 1 1
Sample Output
3 2 3 4 4
题意:给你一棵树,每条边都有一定的权值,然后让你找到每个点所能走到的最远距离
思路:把无根树转化成有根树分析,
对于上面那棵树,要求距结点2的最长距离,那么,就需要知道以2为顶点的子树(蓝色圈起的部分,我们叫它Tree(2)),距顶点2的最远距离L1
还有知道2的父节点1为根节点的树Tree(1)-Tree(2)部分(即红色圈起部分),距离结点1的最长距离+dist(1,2) = L2,那么最终距离结点2最远的距离就是max{L1,L2}
f[i][0],表示顶点为i的子树的,距顶点i的最长距离
f[i][1],表示Tree(i的父节点)-Tree(i)的最长距离+i跟i的父节点距离
f[i][1],表示Tree(i的父节点)-Tree(i)的最长距离+i跟i的父节点距离
要求所有的f[i][0]很简单,只要先做一次dfs求每个结点到叶子结点的最长距离即可。
然后要求f[i][1], 可以从父节点递推到子节点,
假设节点u有n个子节点,分别是v1,v2...vn
那么
如果vi不是u最长距离经过的节点,f[vi][1] = dist(vi,u)+max(f[u][0], f[u][1])
如果vi是u最长距离经过的节点,那么不能选择f[u][0],因为这保存的就是最长距离,要选择Tree(u)第二大距离secondDist,
可得f[vi][1] = dist(vi, u) + max(secondDist, f[u][1])
然后要求f[i][1], 可以从父节点递推到子节点,
假设节点u有n个子节点,分别是v1,v2...vn
那么
如果vi不是u最长距离经过的节点,f[vi][1] = dist(vi,u)+max(f[u][0], f[u][1])
如果vi是u最长距离经过的节点,那么不能选择f[u][0],因为这保存的就是最长距离,要选择Tree(u)第二大距离secondDist,
可得f[vi][1] = dist(vi, u) + max(secondDist, f[u][1])
- struct Node{
- int v, w;
- };
- vector<Node>adj[MAXN];
- int indeg[MAXN];
- int val[MAXN];
- int n, m;
- int64 f[MAXN][2];
- int vis[MAXN];
- int64 dfs1(int u){
- vis[u] = true;
- f[u][0] = 0;
- for(int i=0; i<adj[u].size(); ++i){
- int v = adj[u][i].v;
- int w = adj[u][i].w;
- if(vis[v]) continue;
- f[u][0] = max(f[u][0], dfs1(v)+w);
- }
- return f[u][0];
- }
- void dfs2(int u, int fa_w){
- vis[u] = true;
- int max1=0, v1, max2=0, v2;
- for(int i=0; i<adj[u].size(); ++i){
- int v = adj[u][i].v;
- int w = adj[u][i].w;
- if(vis[v]) continue;
- int tmp = f[v][0] + w;
- if(tmp > max1){
- max2 = max1; v2 = v1;
- max1 = tmp; v1 = v;
- }else if(tmp == max1 || tmp>max2){
- max2 = tmp;
- v2 = v;
- }
- }
- if(u != 1){
- int tmp = f[u][1];
- int v = -1;
- if(tmp > max1){
- max2 = max1; v2 = v1;
- max1 = tmp; v1 = v;
- }else if(tmp == max1 || tmp>max2){
- max2 = tmp;
- v2 = v;
- }
- }
- for(int i=0; i<adj[u].size(); ++i){
- int v = adj[u][i].v;
- int w = adj[u][i].w;
- if(vis[v]) continue;
- if(v==v1){
- f[v][1] = max2 + w;
- }else{
- f[v][1] = max1 + w;
- }
- dfs2(v, w);
- }
- }
- int main(){
- while(~scanf("%d", &n) && n){
- for(int i=1; i<=n; ++i) adj[i].clear();
- for(int u=2; u<=n; ++u){
- int v, w;
- scanf("%d%d", &v, &w);
- adj[u].push_back((Node){v, w});
- adj[v].push_back((Node){u, w});
- }
- memset(f, 0, sizeof(f));
- memset(vis, 0, sizeof(vis));
- dfs1(1);
- memset(vis, 0, sizeof(vis));
- dfs2(1, 0);
- for(int i=1; i<=n; ++i){
- cout << max(f[i][0], f[i][1]) << endl;
- }
- }
- return 0;
- }
http://www.cnblogs.com/kuangbin/archive/2012/08/28/2659915.html
由于对于一个节点来说,可能得到的距离最大的值的路径来自他的子树,或者从他的父节点过来,所以用两次DFS。
第一次DFS求出所有节点在他的子树范围内到叶子节点距离的最大值和第二大的值,第二次DFS更新从父节点过来的情况就可以了。
因为如果只存最大值的话,判断一个点的从父节点过来的最大值,那么如果他的父节点存的最大值正好是从该点过来的,那么就失去了从父节点过来的状态,所以要记录最大的两个值。
http://blog.csdn.net/dgq8211/article/details/10041625
考虑到树中每个点的最远距离其实只有两种情况,从子树 或 从父亲 得到。(Note:from sons or father)
首先从根节点跑一次 dfs,很容易求出 u 到子树中的最远距离和次远距离,解决了 from sons。(Note:from sons of v => from sons of u)
再从上到下对 from father 进行 dp,对于<u,v> 关于 v 的转移既可以从 u 的父亲也可以从 u 的子树得到,解决 from father。(Note:from father of v ≠> from father of u)
而维护次远距离就是为了 from sons of u 时不要取到 v。
最后再取个 max 就好了。
- struct Vertex{
- int head;
- }V[N];
- struct Edge{
- int v,w,next;
- }E[N];
- int top;
- void init()
- {
- memset(V,-1,sizeof(V));
- top = 0;
- }
- void add_edge(int u,int v,int w)
- {
- E[top].v = v;
- E[top].w = w;
- E[top].next = V[u].head;
- V[u].head = top++;
- }
- int dp[N][3];
- void dfs1(int u)
- {
- int biggest = 0 , bigger = 0;
- for(int i=V[u].head;~i;i=E[i].next)
- {
- int v = E[i].v;
- dfs1(v);
- int tmp = dp[v][0]+E[i].w;
- if(biggest <= tmp)
- {
- bigger = biggest;
- biggest = tmp;
- }
- else if(bigger < tmp)
- bigger = tmp;
- }
- dp[u][0] = biggest;
- dp[u][1] = bigger;
- }
- void dfs2(int u)
- {
- for(int i=V[u].head;~i;i=E[i].next)
- {
- int v = E[i].v;
- dp[v][2] = max(dp[u][2] , dp[v][0]+E[i].w==dp[u][0] ? dp[u][1] : dp[u][0]) + E[i].w;
- dfs2(v);
- }
- }
- int main()
- {
- int n;
- while(~scanf("%d",&n))
- {
- init();
- for(int v=2;v<=n;v++)
- {
- int u,w;
- scanf("%d%d",&u,&w);
- add_edge(u,v,w);
- }
- dfs1(1);
- dp[1][2] = 0;
- dfs2(1);
- for(int i=1;i<=n;i++)
- printf("%d\n",max(dp[i][0],dp[i][2]));
- }
- return 0;
- }
任选一点为根节点,先求出每个节点到子节点的最大距离和次大距离,然后更新每个子节点的最大,次大距离,就是把每个子节点当一次根节点,如果它在父节点的最大距离边上,就用次大距离+到父节点的距离与自己的最大次大距离比较
- int n,head[N],num,vis[N],dp[N][2];
- struct edge
- {
- int st,ed,next,w;
- }E[N*2];
- void addedge(int x,int y,int w)
- {
- E[num].st=x;
- E[num].ed=y;
- E[num].w=w;
- E[num].next=head[x];
- head[x]=num++;
- }
- int dfs(int u)
- {
- int i,v;
- vis[u]=1;
- for(i=head[u];i!=-1;i=E[i].next)
- {
- v=E[i].ed;
- if(vis[v]==1)continue;
- int temp=dfs(v)+E[i].w;
- if(temp>dp[u][1])//最长边
- {
- dp[u][0]=dp[u][1];
- dp[u][1]=temp;
- }
- else if(temp>dp[u][0])//次长边
- {
- dp[u][0]=temp;
- }
- }
- return dp[u][1];
- }
- void dfs1(int u)
- {
- vis[u]=1;
- int i,v,temp;
- for(i=head[u];i!=-1;i=E[i].next)
- {
- v=E[i].ed;
- if(vis[v]==1)continue;
- if(dp[v][1]+E[i].w==dp[u][1])//如果改点在父节点的最长边上,就与次长边比较
- temp=dp[u][0]+E[i].w;
- else temp=dp[u][1]+E[i].w;//不在父节点的最长边上,就与最长边比较
- if(temp>dp[v][1])
- {
- dp[v][0]=dp[v][1];
- dp[v][1]=temp;
- }
- else if(temp>dp[v][0])
- {
- dp[v][0]=temp;
- }
- dfs1(v);
- }
- }
- int main()
- {
- int i,x,y,w;
- while(scanf("%d",&n)!=-1)
- {
- memset(head,-1,sizeof(head));
- num=0;
- for(x=2;x<=n;x++)
- {
- scanf("%d%d",&y,&w);
- addedge(x,y,w);
- addedge(y,x,w);
- }
- memset(dp,0,sizeof(dp));
- memset(vis,0,sizeof(vis));
- dfs(1);
- memset(vis,0,sizeof(vis));
- dfs1(1);
- for(i=1;i<=n;i++)
- printf("%d\n",dp[i][0]>dp[i][1]?dp[i][0]:dp[i][1]);
- }
- return 0;
- }
http://blog.163.com/zjut_nizhenyang/blog/static/16957002920112610444983
先以 1 作为根节点进行一次 dfs 遍历,遍历的时候把以 第 i 为根节点往子树方向可以走到的最远距离和次远距离给求出来,且这两个距离是不在同一个分支中的
然后我们进行第二次的从根节点开始dfs遍历,这时候我们要判断的是对于一个节点 i ,除了子树方向上可能有最远距离外,我通过父节点方向是否可以有更加远的距离
,而对于这个操作,我们只要访问父节点所能到达的最远距离,然后接上一段就行了,但是这里会产生一个问题——就是父节点的最远距离可能是会经过当前这个节点
的,因此我们要进行判断当前节点是否在父节点的最远距离的分支上
如果在的话,那么我们要换一个分支,且要是最远的,这个其实就是第一次dfs中求出的与最远路径不在同一分支上的次远路径,我们接上这段进行转移
如果不在的话,那么我们直接接上父节点的最远路径进行转移就行了