light oj 1257 Farthest Nodes in a Tree (II)(724训练题目)主要是利用了反证法: 假设 s-t这条路径为树的直径,或者称为树上的最长路 现有结论,从任意一点

主要是利用了反证法:

假设 s-t这条路径为树的直径,或者称为树上的最长路

现有结论,从任意一点u出发搜到的最远的点一定是s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路

证明:

1    设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则

dis(u,T) >dis(u,s)     且  dis(u,T)>dis(u,t)   则最长路不是s-t了,与假设矛盾

2   设u不为s-t路径上的点

    首先明确,假如u走到了s-t路径上的一点,那么接下来的路径肯定都在s-t上了,而且终点为s或t,在1中已经证明过了

    所以现在又有两种情况了:

    1:u走到了s-t路径上的某点,假设为X,最后肯定走到某个端点,假设是t ,则路径总长度为dis(u,X)+dis(X,t)

    2:u走到最远点的路径u-T与s-t无交点,则dis(u-T) >dis(u,X)+dis(X,t);显然,如果这个式子成立,

    则dis(u,T)+dis(s,X)+dis(u,X)>dis(s,X)+dis(X,t)=dis(s,t)最长路不是s-t矛盾

    附上一张第二种情况的图

     



这道题让你求出距离每个点最远的点之间距离是多少,因为每个点走的最长路的重点肯定是直径上的某个端点,所以,写个bfs不断的搜吧

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<vector>
#define N 30005
#define in push_back
using namespace std;
vector<int>e[N];
vector<int>d[N];
int k,u,v,w,n,t;
int d1[N],d2[N];
bool b[N];
void bfs(int s,int &t,int w[])
{
   queue<int>q;int mx=0;
   memset(b,0,sizeof(b));
   memset(w,0,sizeof(w));
   q.push(s);b[s]=1;w[s]=0;
   while(!q.empty())
   {
      int now=q.front();q.pop();
      for(int i=0;i<e[now].size();i++)
      {
          v=e[now][i];if(b[v])continue;
          {
             w[v]=w[now]+d[now][i];
             b[v]=1;q.push(v);
             if(w[v]>mx) mx=w[v],t=v;
          }
      }
   }
}

int main()
{
    scanf("%d",&t);
    k=0;
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
          {e[i].clear();d[i].clear();}
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            e[u].in(v);e[v].in(u);
            d[u].in(w);d[v].in(w);
        }
        memset(d1,0,sizeof(d1));
        memset(d2,0,sizeof(d2));
        int x,y;
        bfs(0,x,d1);
        bfs(x,y,d1);
        bfs(y,x,d2);
        printf("Case %d:\n",++k);
        for(int i=0;i<n;i++)
        {
            printf("%d\n",max(d1[i],d2[i]));
        }
    }
}



方法2:对于每个点,维护一个最长路一个次长路,起初,由叶节点向根结点更新,记录每个根结点是由哪个子节点更新的最长路,然后再做一遍dfs由根结点向子节点更新,对于一个子节点i,若其父节点的最长路是由其更新的,那么在更新i的最、次长路时注意交叉问题,不需要加i到父节点边长的不用加,对于每个i依然保留更新i最长路的父节点,若i不是更新父节点最长路的节点,方法照旧,然后依次向下dfs。


代码来自某大神~~~~

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
struct edge
{
    int v;
    int w;
    int next;
}edge[60010];
int head[30005];
int tot = 1;
void add(int u,int v,int w)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    edge[tot].w=w;
    head[u]=tot++;
}
int t,n,u,v,w;
int vis[30005];
int l1[30004],l2[30005];
long long cost1[30004],cost2[30004];
int num[30004];

void dfs(int u,int p)
{
    l1[u]=0;
    l2[u]=0;
    cost1[u]=0;
    cost2[u]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        int w = edge[i].w;
        if(v==p) continue;
        dfs(v,u);
        if(cost2[u]<cost1[v]+w)
        {

            cost2[u]=cost1[v]+w;
            if(cost1[u]<cost2[u])
            {
               // swap(l1[u],l2[u]);
                swap(cost1[u],cost2[u]);
                num[u]=v;
            }
        }
    }
}
void dfs2(int u,int p)
{
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        int w = edge[i].w;
        if(v==p) continue;
        if(v==num[u])
        {
            if(cost2[v]<cost2[u]+w)
            {

                cost2[v]=cost2[u]+w;
                if(cost1[v]<cost2[v])
                {
                   // swap(l1[v],l2[v]);
                    swap(cost1[v],cost2[v]);
                    num[v]=u;
                }
            }
        }
        else
        {
            if(cost2[v]<cost1[u]+w)
            {
               // l2[v]=l1[u]+1;
                cost2[v]=cost1[u]+w;
                  if(cost1[v]<cost2[v])
                {
                    //swap(l1[v],l2[v]);
                    swap(cost1[v],cost2[v]);
                    num[v]=u;
                }
            }
        }
        dfs2(v,u);
    }
}
int main()
{
   scanf("%d",&t);
   int Case = 1;
   while(t--)
   {
       scanf("%d",&n);

       tot=1;
       memset(head,-1,sizeof(head));
       for(int i=1;i<n;i++)
       {
           scanf("%d%d%d",&u,&v,&w);
           add(u,v,w);
           add(v,u,w);
       }
       dfs(0,-1);
       dfs2(0,-1);
       printf("Case %d:\n",Case++);
       for(int i=0;i<n;i++)
       {           //printf("%d %lld %lld \n",i,cost1[i],cost2[i]);
           printf("%lld\n",cost1[i]);
       }
   }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值