poj1985 Cow Marathon(树的直径)

Cow Marathon

Time Limit: 2000MS   Memory Limit: 30000K
Total Submissions:9653   Accepted: 4411
Case Time Limit: 1000MS

Description

After hearing about the epidemic of obesity in the USA, Farmer John wants his cows to get more exercise, so he has committed to create a bovine marathon for his cows to run. The marathon route will include a pair of farms and a path comprised of a sequence of roads between them. Since FJ wants the cows to get as much exercise as possible he wants to find the two farms on his map that are the farthest apart from each other (distance being measured in terms of total length of road on the path between the two farms). Help him determine the distances between this farthest pair of farms. 

Input

* Lines 1.....: Same input format as "Navigation Nightmare".

Output

* Line 1: An integer giving the distance between the farthest pair of farms. 

Sample Input

7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S

Sample Output

52

Hint

The longest marathon runs from farm 2 via roads 4, 1, 6 and 3 to farm 5 and is of length 20+3+13+9+7=52. 

Source

USACO 2004 February

题意:求树的直径,模板题

求树的直径有两种方式

(1)树形dp求法讲解:https://saoka.blog.luogu.org/shu-di-zhi-jing

数组定义:

dp[x]:以 x 为根的子树中,与 x 最远的节点到 x 的距离

f[x]:经过点 x 的所有链中最长一条的长度

edge(u,v):从节点 u 到节点 v 的路径长度

思路:

  • 从叶子节点开始向上合并,找出对于每一个节点 x 的 d[x] ,具体操作可以通过枚举 x 的每条边,然后通过转移方程转移:

    dp[x] = max(dp[v_i]+edge(x,v_i), dp[x])

  • 然后考虑怎么求 f[x]。若设 v1​、 v2​ 分别为从节点 x 出发能走到的最远、次远节点,那么我们可以轻松得到,经过 x 的最长链是由 (x,v1​) 和 (x,v2​) 组成的。所以我们在求 d[x] 的时候,如果找到了一个点能够更新 d[x] ,那么我们就把这两个 d[] 值相加,这样一定可以保证能够取到最远点和次远点。

//树形dp
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

int head[N], tot, ans;
int dp[N]; //以x为根的子树中,与x最远的节点到x的距离
bool vis[N];

struct Edge {
    int to, w, next;
} edge[M];

void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int w) {
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void dfs(int u) {
    vis[u] = 1;
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if(vis[v]) continue;
        dfs(v);
        //设v1、v2分别为从节点x出发能走到的最远、次远节点,那么经过x的最长链是由(x,v1)和(x,v2)组成的
        //在求d[x]的时候,如果找到了一个点能够更新d[x], 那么就把这两个d[]值相加,这样一定可以保证能够取到最远点和次远点
        ans = max(ans, dp[u] + dp[v] + edge[i].w);
        dp[u] = max(dp[u], dp[v] + edge[i].w);
    }
}

int main() {
    char c;
    int n, m, u, v, w;
    init();
    scanf("%d%d", &n, &m);
    while(m--) {
        scanf("%d%d%d", &u, &v, &w);
        cin >> c;
        addedge(u, v, w);
        addedge(v, u, w);
    }
    for(int i = 1; i <= n; ++i) vis[i] = 0;
    for(int i = 1; i <= n; ++i) dp[i] = 0;
    dfs(1);
    printf("%d\n", ans);
    return 0;
}

(2)两次dfs求法讲解:https://www.cnblogs.com/handsome-zyc/p/11237529.html

方法:先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点W,W到Q的距离就是是的直径

证明如下:

①若P已经在直径上,根据树的直径的定义可知Q也在直径上且为直径的一个端点

②若P不在直径上,我们用反证法,假设此时WQ不是直径,AB是直径

--->若AB与PQ有交点C,由于P到Q最远,那么PC+CQ>PC+CA,所以CQ>CA,易得CQ+CB>CA+CB,即CQ+CB>AB,与AB是直径矛盾,不成立,如下图(其中AB,PQ不一定是直线,画成直线是为了方便):

--->若AB与PQ没有交点,M为AB上任意一点,N为PQ上任意一点。首先还是NP+NQ>NQ+MN+MB,同时减掉NQ,得NP>MN+MB,易知NP+MN>MB,所NP+MN+MA>MB+MA,

即NP+MN+MA>AB,与AB是直径矛盾,所以这种情况也不成立,如下图:

//两次dfs
//先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点W,W到Q的距离就是是的直径
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

int head[N], tot, dis[N], ans, id;

struct Edge {
    int to, w, next;
} edge[M];

void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int w) {
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void dfs(int u, int fa) {
    if(ans < dis[u]) {
        ans = dis[u];
        id = u;
    }
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if(v == fa) continue;
        dis[v] = dis[u] + edge[i].w;
        dfs(v, u);
    }
}

int main() {
    char c;
    int n, m, u, v, w;
    init();
    scanf("%d%d", &n, &m);
    while(m--) {
        scanf("%d%d%d", &u, &v, &w);
        cin >> c;
        addedge(u, v, w);
        addedge(v, u, w);
    }
    for(int i = 1; i <= n; ++i) dis[i] = 0;
    ans = 0;
    dfs(1, 0);
    ans = 0, dis[id] = 0;
    dfs(id, 0);
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43871207/article/details/110562719