湘潭OJ-1267-树的直径-dp求法

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1267
参考博客:
http://blog.csdn.net/lifelikes/article/details/72058123#reply
这道题卡了我小半个月,最后发现错误竟然是不能用lld,在湘潭oj只能用i64d。。55
不过收获也是蛮大的。
比较重要的地方
1 第一次dfs用后跟遍历,以求得到一条最长路。这时候求得是点为起点的时候的 最长路
2 第二次是求点为终点的时候的最长路。注意。在第一次dfs的时候我们已经记录了他的为起点的方向(用的maxid啊大哥哈哈,maxid就是,smaxid不是,是可以改变的)
如果这时候我们 仍然用maxn[u] 来更新,就会重复计算u和v之间的距离。
所以我们只能用smaxn[u]来更新,
但是在其他情况可以随意更新。
Highway
In ICPCCamp there were towns conveniently numbered with connected with roads. The -th road connecting towns and has length . It is guaranteed that any two cities reach each other using only roads.

Bobo would like to build highways so that any two towns reach each using only highways. Building a highway between towns and costs him cents, where is the length of the shortest path between towns and using roads.

As Bobo is rich, he would like to find the most expensive way to build the highways.

Input
The input contains zero or more test cases and is terminated by end-of-file. For each test case:

The first line contains an integer . The -th of the following lines contains three integers , and .

The number of test cases does not exceed .
Output
For each test case, output an integer which denotes the result.

Sample Input
5
1 2 2
1 3 1
2 4 2
3 5 1
5
1 2 2
1 4 1
3 4 1
4 5 2
Sample Output
19
15

Source
XTU OnlineJudge
题意:给的那个n个城镇,有n-1个道路相连,现在需要修建n-1条高速公路(一条高速公路为一个边集),要求每两个城镇都可以通过高速公路连接并且总花费最大。
方法:首先是每两个城镇都可以通过高速公路连接。
如果我们将每两个点的连接边都计算一次,固然最大,但是题目要求是n-1。
这样思考到 树的最长简单路径,即树的直径,考虑将每一个点向树的直径中较远的一段建高速公路,即达到最后结果。并且可以实现每两个点的连接。
但是有一个要注意的地方
在这里插入图片描述
可见,1,2,3,5,点的修建距离 都不如树的直径大,
所以每个点建造距离是 4 4 5 6 6
而要求结果最贵并且可以达到每两个点连接,如果我们去掉最小的4(1-4的建造),那么我们将无法达到1与其他点 使用高速公路进行连接的条件,而树的直径去掉了两次,去掉一个无妨。故答案要去掉一个树的直径(每一个端点都会计算一次。)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int MAXN=100102;
struct Node
{
    int to;
    int next;
    long long  len;
}edge[MAXN*3];//因为存无向边,所以需要2倍
int head[MAXN];//头结点i  tol;
long long  maxn[MAXN];//该节点往下到叶子的最大距离
long long smaxn[MAXN];//次大距离
long long  maxid[MAXN];//最大距离对应的序号
long long  smaxid[MAXN];//次大的序号
int tol;
void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
}

void add(int a,int b,long long len)
{
    edge[tol].to=b;
    edge[tol].len=len;
    edge[tol].next=head[a];
    head[a]=tol++;
    edge[tol].to=a;
    edge[tol].len=len;
    edge[tol].next=head[b];
    head[b]=tol++;
}

//求结点v往下到叶子结点的最大距离
//p是v的父亲结点
void dfs1(int u,int p)
{
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(edge[i].to==p) continue;
        dfs1(edge[i].to,u);
        if(maxn[edge[i].to]+edge[i].len>smaxn[u]){
            smaxn[u]=maxn[v]+edge[i].len;//用最长边来更新
            smaxid[u]=v;
            if(smaxn[u]>maxn[u]){
                swap(smaxn[u],maxn[u]);
                swap(smaxid[u],maxid[u]);
            }
        }
    }
}
//p是u的父亲结点,len是p到u的长度
void dfs2(int u,int p)
{   for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;//这是尾巴;
        if(v==p) continue;
        if(maxid[u]==v){//现在我们注意,这个路径可不能重来啊
                if(smaxn[u]+edge[i].len>smaxn[v]){
                    smaxn[v]=smaxn[u]+edge[i].len;
                    smaxid[v]=u;
                     if(smaxn[v]>maxn[v]){
                        swap(smaxn[v],maxn[v]);
                        swap(maxid[v],smaxid[v]);
                     }
                }
        }
        else{
            if(maxn[u]+edge[i].len>smaxn[v]){
                smaxn[v]=maxn[u]+edge[i].len;
                smaxid[v]=u;
                if(smaxn[v]>maxn[v]){
                    swap(smaxn[v],maxn[v]);
                    swap(maxid[v],smaxid[v]);
                }
            }
        }
        dfs2(v,u);
   }
}
int main()
{
    int n;
    int v,a,b;
    long long len;
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(int i=2;i<=n;i++)
        {
            scanf("%d%d%I64d",&a,&b,&len);
            add(a,b,len);
        }
        memset(smaxn,0,sizeof(smaxn));
        memset(maxn,0,sizeof(maxn));
        dfs1(1,-1);
        dfs2(1,-1);
        long long x=-1;
        long long all=0;
         for(int i=1;i<=n;i++)
      x=max(x,maxn[i]+smaxn[i]);//减去一个树的直径
        for(int i=1;i<=n;i++)
         all+=maxn[i];
         printf("%I64d\n",all-x);

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值