学习记录3:树的直径(树的最长链)(poj 1985 Cow Marathon)

15 篇文章 0 订阅
8 篇文章 0 订阅

树的直径是指树上的最长链(如果是要节点最多的链那就边权作为1处理)

 

如下图所示,我们发现,对于每个节点(叶子除外)如果它是最长链上的点,那么最长链等于以这个节点为根时的最长的两条子链之和。因此,对于求最长链,我们可以转化成求树中的每个节点的最长的两个子链之和的问题;

这样一来,对于每个节点情况就有3种                                                                                   :

1. 最长链恰好就是他的两条最长子链之和

2. 最长链是他的最长子链和其与父亲相连的那条链的     最大值之和             

3. 这个点不在最长链上                                                                                                                                                                 

  

因此我们可以做一次DFS+DP

 

实际上如果这个节点是非根节点时,他通往父节点的那条链在DFS时并未算作他的子链,但是,换个角度,他可以是他父节点的子链,这样以来情况2的情况也是满足的。

 

于是 我们需要保存的节点信息是这个节点最长的两个子链长度,没有就是0

接着 比较当前的最长链的值 与 此次两条子链的大小之和 判断是否更新当前最长链的值

DFS结束后的就是答案;



另外 还有另一种求最长链的方式

随意从一个点出发BFS找离这个点最远的那个点,下一次从找到的那个点再做一次BFS找最远点,而第二次的路径长度就是最长链的值。

关于这个问题,我们只要证明第一次BFS出来的那个点N是最长链上的起点(或者终点);

因为BFS1求的是最长链,所以最后找到的那个点一定是一片叶子,也就是某条链的起点(或者终点);

因此

(假设1)我们只要证明在第一次BFS的时候能够通过最长链上的点P则就能证明第一次BFS出来的那个点是最长链的起点(或者终点);(因为假设通过点P而N不是最长链上的点,那么P到N点的值将会大于他到已知最长链上的某个末尾的值,那么这条链就和已知最长链矛盾,所以假设1是成立的)

 

假设2 树上的任意一个点做BFS1的时候都会至少经过一个最长路上的点;

如图,我们假设已知的最长链(蓝色的点)的长度 LENMAX=F+K;起始点为红色S,以及绿色的不是最长链上的点Q;K为P->N之间的长度(以此类推其他边上的字母)

那么假设某个点做BFS1时不经过P点,由BFS的优先关系作为参考,可以得出

M+R>M+X+K =>R>X+K => F+X+R>(F+K)+2X=LENMAX+2X 与已知条件矛盾 ,假设2成立.

 

例子是poj 1985 Cow Marathon

点击打开链接

题目的意思就是一棵树的最长链

思路差不多就是上面那样

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<bitset>
#define pi acos(-1.0)
#define inf 1<<29
#define INF 0x3f3f3f3f
#define zero 1e-8

const int li[] = { -1, 0, 1, 0};
const int lj[] = {0, -1, 0, 1};

using namespace std;

const int N = 4e4 + 7;

int maxlen;//最长链的长度

bool flag[N];

struct Node {

    int max1, max2; //子链中最长的两条链

} tree[N];

vector<int>  edge[N];
vector<int>  lens[N];

void init(int n)
{

    maxlen = 0;
    memset(flag, false, sizeof(flag));

    for (int i = 0; i <= n; ++i) {
        edge[i].clear();
        lens[i].clear();
        tree[i].max1 = tree[i].max2 = 0;
    }
}

void dfs(int node)
{

    int max1 = 0, max2 = 0;
    int tag = 0;

    for (int i = 0; i < edge[node].size(); ++i) {

        if (flag[edge[node][i]])  continue;
        tag++;
        flag[edge[node][i]] = true;
        dfs(edge[node][i]);
        int childlen = tree[edge[node][i]].max1 + lens[node][i];
        if (max1 < max2) {
            if (max1 < childlen) max1 = childlen;
        } else if (max2 < childlen) {
            max2 = childlen;
        }



    }

    int maxx = max1 > max2 ? max1 : max2;
    int minn = max1 > max2 ? max2 : max1;

    if (!tag) {
        tree[node].max1 = tree[node].max2 = 0;
    } else if (tag == 1) tree[node].max1 = maxx, tree[node].max2 = 0;
    else {
        tree[node].max1 = maxx ;
        tree[node].max2 = minn ;
    }
    maxlen = maxlen > (tree[node].max1 + tree[node].max2) ?
             maxlen : (tree[node].max1 + tree[node].max2);
}
int main()
{

    int n, m;
    while(~scanf("%d %d", &n, &m)) {
        init(n);
        int l, r, len;

        int id;
        for (int i = 0; i < m; ++i) {

            char c;
            scanf("%d %d %d %c", &l, &r, &len, &c);

            edge[l].push_back(r);
            edge[r].push_back(l);
            lens[l].push_back(len);
            lens[r].push_back(len);
        }

        flag[1] = true;

        dfs(1);

        printf("%d\n", maxlen);
    }
    return 0;
}


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值