POJ 1849 Two(遍历树)

http://poj.org/problem?id=1849

题意:

有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 可是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少?

分析:

首先本题仅仅要求出树的直径, 然后用树的总长sum*2-树的直径就是所求结果.
以下一步步来说明为什么是这种.

1.如果仅仅有1个机器人遍历树,且要求回到原点,
它最少须要走多少路?

答: 它须要走树总长sum的两倍, 即每条树边它都要走两次才行. 这个结论画个图就明确了, 对于每条边, 机器人要走过该边, 之后还要从该边回去(不回来就不能回到出发点了). 所以自然是sum*2.

2.如果1问中的机器人遍历树,可是不要求它回到原点,
那么它最少须要走多少路?

答: 最少须要走sum-[从出发点能走到最远的点的距离]. 在行走的过程中每一个分叉, 它走过去,又走回来就可以. 能够反证得出.

3.如果有两个机器人从s出发,遍历整个树且终于回到出发点.
它们行走的最短距离是?

答: 树总长的两倍. 每一个机器人都必须回到原点, 那么必定每条边至少要被走两次.

4.如果有两个机器人从s出发,遍历整个树且它们不须要回到出发点.
它们行走的最短距离是?

答: 树总长的两倍-树的直径. 机器人出去不回来,则所走路径中有一条简单路径是能够仅仅走一遍的,派出了两个点去遍历,也就是说有两条简单路径是能够直走一边的,我们要使这两条简单路径的总和尽可能的长,就转换为了树的最长路径问题了.

注意:上面第4种情况, 两个机器人从哪点出发都是没有不论什么差别的. 由于假设它们出发点不在树的直径上, 那么它们一定能够一起移动到树直径上的某个点上,然后分别朝树直径的两个方向走, 而且遍历它们走的树直径的全部分叉路两次.

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100000+5;

//边结构
struct Edge
{
    Edge(){}
    Edge(int to,int cost,int next):to(to),cost(cost),next(next){}
    int to;
    int cost;
    int next;
}edges[maxn];
int cnt=0;//总边数
int head[maxn];

//加入两条有向边
void AddEdge(int u,int v,int cost)
{
    edges[cnt]=Edge(v,cost,head[u]);
    head[u]=cnt++;
    edges[cnt]=Edge(u,cost,head[v]);
    head[v]=cnt++;
}

int dist[maxn];

//返回从s能到达的最长点编号
int BFS(int s)
{
    int max_dist=0;
    int id=s;
    queue<int> Q;
    memset(dist,-1,sizeof(dist));
    dist[s]=0;
    Q.push(s);

    while(!Q.empty())
    {
        int u=Q.front(); Q.pop();
        if(dist[u]>max_dist)
            max_dist=dist[id=u];

        for(int i=head[u]; i!=-1; i=edges[i].next)
        {
            Edge &e=edges[i];
            if(dist[e.to]==-1)
            {
                dist[e.to]=dist[u]+e.cost;
                Q.push(e.to);
            }
        }
    }
    return id;
}

int main()
{
    int n,s;
    while(scanf("%d%d",&n,&s)==2)
    {
        int sum=0;//树的总长
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=1;i<=n-1;i++)
        {
            int u,v,cost;
            scanf("%d%d%d",&u,&v,&cost);
            sum+=cost;
            AddEdge(u,v,cost);
        }

        printf("%d\n",sum*2-dist[BFS(BFS(s))]);
    }
    return 0;
}
时间: 2024-10-28 14:26:28

POJ 1849 Two(遍历树)的相关文章

POJ 2481 Cows (线段树)

Cows 题目:http://poj.org/problem?id=2481 题意:有N头牛,每只牛有一个值[S,E],如果对于牛i和牛j来说,它们的值满足下面的条件则证明牛i比牛j强壮:Si <=Sjand Ej <= Ei and Ei - Si > Ej - Sj.现在已知每一头牛的测验值,要求输出每头牛有几头牛比其强壮. 思路:将牛按照S从小到大排序,S相同按照E从大到小排序,这就保证了排在后面的牛一定不比前面的牛强壮.再按照E值(离散化后)建立一颗线段树(这里最值只有1e5,所

POJ 2299 离散化线段树

点击打开链接 Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 40827   Accepted: 14752 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by

POJ训练计划2299_Ultra-QuickSort(线段树/单点更新)

解题报告 题意: 求逆序数. 思路: 线段树离散化处理. #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #define LL long long using namespace std; LL sum[2001000],num[501000],_hash[501000]; void push_up(int rt) { sum[rt]=sum[rt*2

POJ 3667(线段树区间合并)

http://poj.org/problem?id=3667 题意:两个操作 : 1 选出靠左的长度为a的区间. 2 把从 a到a+b的区间清空. 线段树区间合并+lazy // by caonima // hehe #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> using namespace std; co

hdu 1541/poj 2352:Stars(树状数组,经典题)

Stars Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4052    Accepted Submission(s): 1592 Problem Description Astronomers often examine star maps where stars are represented by points on a plan

POJ Code the Tree 树的pufer编号

Code the Tree Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 2259   Accepted: 859 Description A tree (i.e. a connected graph without cycles) with vertices numbered by the integers 1, 2, ..., n is given. The "Prufer" code of such a

js39---组合模式,查找遍历树

/** *有这样一个需求 *有一个学校有2个班(一班,二班) *每个班级分2个小组(一班一组,一班二组,二班一组,二班二组) *学校计算机教室有限,每一个小组分着来上课. *考试的时候大家一起考 *请用程序来模拟这个需求 */ (function(){ //不用组合模式 //学校类 var school = function(name){ this.name = name; //班级 var classes = new Array(); this.addClasses = function(cl

POJ 2892 Tunnel Warfare (树状数组+二分)

题目大意: 三个操作 D pos  将pos位置摧毁,让它和周围不相连. Q pos 问和pos 相连的有多少个村庄. R 修复最近摧毁的村庄. 思路分析: 树状数组记录这个区间有多少个1. 如果  [s-e] 有e-s+1个1 的话.那么这个区间是相连的. 这样的话,我们就可以用二分的办法求出与某个位置最大相连的数量. 还有这里二分 while(l<=r) { if(满足) { ans=mid; l=mid+1; } else r=mid-1; } #include <cstdio>

创建先序二叉树-创建层次遍历树

创建先序二叉树 #include<iostream> using namespace std; class BinTreeNode { public:     char ch;     BinTreeNode(int value){ch=value;}     BinTreeNode *left,*right; }; BinTreeNode* create_tree() {     char item;     BinTreeNode *t,*t_l,*t_r;     cin>>