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

来源:互联网 发布:sql 逐行累加 编辑:程序博客网 时间:2024/05/21 11:10

主要是利用了反证法:

假设 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_backusing 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
原创粉丝点击