POJ 1849 - Two

我们知道,树都有一条直径,那么,

①如果我们的起始点在直径上,那么很显然的,两辆车分别朝两个方向走,遇到直径上的分叉,就要花费分叉的总长*2的路程来遍历这些分叉,而直径上的边都只需要走一遍即可

②而如果我们的起始点不在直径上,那么他们两辆车只需要先把这个分叉遍历一遍,最后回到直径上,那么又可以按①的过程,两辆车朝两边走。

综上,我们就可以知道,这样需要走过的总路程 ans = "直径长度" + 2 * "所有分叉上边的长度总和",换句话说,就是ans = 2 * "所有边的长度总和" - "直径长度";

至于,求树的直径,我们可以有两种方法:

参考:http://blog.csdn.net/tc_to_top/article/details/47002255

首先是两次DFS的过程:

 1 #include<cstdio>
 2 #include<vector>
 3 #define MAXN 100000+5
 4 using namespace std;
 5 struct Edge{
 6     int u,v,w;
 7 };
 8 vector<Edge> adj[MAXN];
 9 int n,s,d[MAXN],sum;
10 void dfs(int now,int par)
11 {
12     for(int i=0;i<adj[now].size();i++)
13     {
14         Edge edge=adj[now][i];
15         int next=edge.v;
16         if(next==par) continue;
17         d[next]=d[now]+edge.w;
18         dfs(next,now);
19     }
20 }
21 int main()
22 {
23     while(scanf("%d%d",&n,&s)!=EOF)
24     {
25         sum=0;
26         for(int i=1;i<=n;i++) adj[i].clear();
27         for(int i=1;i<n;i++)
28         {
29             int u,v,w;
30             scanf("%d%d%d",&u,&v,&w);
31             sum+=w;
32             adj[u].push_back((Edge){u,v,w});
33             adj[v].push_back((Edge){v,u,w});
34         }
35         d[s]=0;
36         dfs(s,-1);
37         int max=0,max_i;
38         for(int i=1;i<=n;i++)
39         {
40             if(max<d[i])
41             {
42                 max=d[i];
43                 max_i=i;
44             }
45         }
46         d[max_i]=0;
47         dfs(max_i,-1);
48         int diameter=0;
49         for(int i=1;i<=n;i++) if(diameter<d[i]) diameter=d[i];
50         printf("%d\n",2*sum-diameter);
51     }
52 }

至于树形DP的方法,比较难想,但其实和两次DFS异曲同工,任何一个点,可以在DFS过程中转到其所在分叉链接在直径的那个节点上,而这个节点的最大和次大相加,就可以得到直径。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<vector>
 4 #define MAXN 100000+5
 5 using namespace std;
 6 struct Edge{
 7     int u,v,w;
 8 };
 9 vector<Edge> adj[MAXN];
10 int n,s,sum,diameter;
11 int dp[MAXN][2];
12 void dfs(int now,int par)
13 {
14     for(int i=0;i<adj[now].size();i++)
15     {
16         Edge edge=adj[now][i];
17         int next=edge.v;
18         if(next==par) continue;
19         dfs(next,now);
20         if(dp[now][0] < dp[next][0]+edge.w) // ( "其某个孩子的最大"+"其与孩子的距离" ) > "最大" > "次大"
21         {
22             dp[now][1] = dp[now][0];
23             dp[now][0] = dp[next][0] + edge.w;
24         }
25         else if(dp[now][1] < dp[next][0]+edge.w) // "最大" > ( "其某个孩子的最大"+"其与孩子的距离" ) > "次大"
26         {
27             dp[now][1] = dp[next][0]+edge.w;
28         }
29     }
30     if(diameter<dp[now][0]+dp[now][1]) diameter=dp[now][0]+dp[now][1];
31 }
32 int main()
33 {
34     while(scanf("%d%d",&n,&s)!=EOF)
35     {
36         diameter=sum=0;
37         for(int i=1;i<=n;i++) adj[i].clear();
38         memset(dp,0,sizeof(dp));
39         for(int i=1;i<n;i++)
40         {
41             int u,v,w;
42             scanf("%d%d%d",&u,&v,&w);
43             sum+=w;
44             adj[u].push_back((Edge){u,v,w});
45             adj[v].push_back((Edge){v,u,w});
46         }
47         dfs(s,-1);
48         printf("%d\n",2*sum-diameter);
49     }
50 }
时间: 2024-10-11 12:58:55

POJ 1849 - Two的相关文章

POJ 1849 Two(遍历树)

http://poj.org/problem?id=1849 题意: 有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 可是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少? 分析: 首先本题仅仅要求出树的直径, 然后用树的总长sum*2-树的直径就是所求结果. 以下一步步来说明为什么是这种. 1.如果仅仅有1个机器人遍历树,且要求回到原点, 它最少须要走多少路? 答: 它须要走树总长sum的两倍, 即每条树边它都要走两次才行.

POJ 1849 Two (树形dp 树的直径 两种方法)

Two Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 1232   Accepted: 619 Description The city consists of intersections and streets that connect them. Heavy snow covered the city so the mayor Milan gave to the winter-service a list of st

POJ 1849 树的直径 Two

如果一个点开始遍历一棵树再回到原点那么每条边走两次. 现在是两个人从同一点出发,那么最后遍历完以后两人离得越远越好. 最后两人所处位置的路径上的边走了一次,其他边走了两次. 要使总路程最小,两人最后停在直径两端. 所以最终答案就是总权值*2 - 树的直径 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <vector> 5 #include <algor

POJ 1849 Two(树的直径--树形DP)(好题)

大致题意:在某个点派出两个点去遍历所有的边,花费为边的权值,求最少的花费 思路:这题关键好在这个模型和最长路模型之间的转换,可以转换得到,所有边遍历了两遍的总花费减去最长路的花费就是本题的答案,要思考,而且答案和派出时的起点无关 求最长路两遍dfs或bfs即可,从任意点bfs一遍找到最长路的一个终点,再从这个终点bfs找到起点 //1032K 79MS C++ 1455B #include<cstdio> #include<iostream> #include<cstring

POJ - 3186 Treats for the Cows (区间DP)

题目链接:http://poj.org/problem?id=3186 题意:给定一组序列,取n次,每次可以取序列最前面的数或最后面的数,第n次出来就乘n,然后求和的最大值. 题解:用dp[i][j]表示i~j区间和的最大值,然后根据这个状态可以从删前和删后转移过来,推出状态转移方程: dp[i][j]=max(dp[i+1][j]+value[i]*k,dp[i][j-1]+value[j]*k) 1 #include <iostream> 2 #include <algorithm&

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

POJ——T2271 Guardian of Decency

http://poj.org/problem?id=2771 Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 5932   Accepted: 2463 Description Frank N. Stein is a very conservative high-school teacher. He wants to take some of his students on an excursion, but he is

POJ——T2446 Chessboard

http://poj.org/problem?id=2446 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 18560   Accepted: 5857 Description Alice and Bob often play games on chessboard. One day, Alice draws a board with size M * N. She wants Bob to use a lot of c

poj 1088 滑雪 DP(dfs的记忆化搜索)

题目地址:http://poj.org/problem?id=1088 题目大意:给你一个m*n的矩阵 如果其中一个点高于另一个点 那么就可以从高点向下滑 直到没有可以下滑的时候 就得到一条下滑路径 求最大的下滑路径 分析:因为只能从高峰滑到低峰,无后效性,所以每个点都可以找到自己的最长下滑距离(只与自己高度有关).记忆每个点的最长下滑距离,当有另一个点的下滑路径遇到这个点的时候,直接加上这个点的最长下滑距离. dp递推式是,dp[x][y] = max(dp[x][y],dp[x+1][y]+