zoukankan      html  css  js  c++  java
  • java实现第四届蓝桥杯大臣的旅费

    大臣的旅费

    题目描述
    很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

    为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。
    
    J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。
    
    聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。
    
    J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
    

    输入格式:
    输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数
    城市从1开始依次编号,1号城市为首都。
    接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)
    每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。

    输出格式:
    输出一个整数,表示大臣J最多花费的路费是多少。

    样例输入:
    5
    1 2 2
    1 3 1
    2 4 5
    2 5 4

    样例输出:
    135

    样例说明:
    大臣J从城市4到城市5要花费135的路费。

    根据资源限制尽可能考虑支持更大的数据规模。

    资源约定:
    峰值内存消耗(含虚拟机) < 64M
    CPU消耗 < 5000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
    注意:主类的名字必须是:Main,否则按无效代码处理。

      PS:
        这道题就是求一棵树间两点最长距离,即树的直径。具体求法为 先从根节点出发用dfs求得距离根节点最远的节点,设为u,再从u点出发,用dfs求得距离u最远的节点,设为v,则d[u][v]即u,v节点的距离就为树的直径。
    
    证明如下:(不是我证明的)
    
    为了阐述清楚证明,首先作如下严格定义:
    1。我们用a~b表示树中任意两个结点a,b之间的唯一路径,a~b之间可以有0个或多个结点;
    
    用x in a~b表示结点x处于路径a,b上,即存在形如a~x~b的路径(这里x可以和a或b重合);
    
    用符号a-b表示a,b直接相邻。
    
    
    定理5: 设r是树T的根,u是距离r最远的结点,v是距离u最远的结点。则树的直径就是d(u, v)。
    
    
    证明:设a, b是除了u,v以外的另外两个叶节点。设x = f(f(a, b), u)。即x是a,b,u三个节点的最近公共祖先。
    
    
    根据引理4,一定有 x in u~a 或 x in u~b。不妨设x in u~b 成立。
    于是就有u~x~b这条路径,即
       d(u,b) = d(u,x)+d(x,b) ......(1)
    于是
        d(r,u) >= d(r,a)                    // 因为u是距离r最远的点
    ==> d(r,x) + d(x,u) >= d(r,x) + d(x,a)  // 因为根据公共祖先的定义,x in r~u 且 x in r~a
    ==> d(u,x) >= d(x,a) ........
    
    (2)于是
    d(u,v) >= d(u,b)          // 因为v是距离u最远的点
       = d(u,x)+ d(x,b) // 根据(1)式
      >= d(x,a) + d(x,b) // 根据(2)式  
      >= d(a,b)          // 根据引理2
    
    
    所以对于除了u,v外任意的叶节点a,b,总有d(u, v)>= d(a,b)。
    如果a,b中有一个是u,v之一,显然也有d(u, v)>=d(a,b)。
    再根据引理1和树的半径的定义,可知d(u,v)就是T的直径。
    
     
     
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Scanner;
     
    //动态链表ArrayList
    class Vertex{
    	ArrayList<Integer> V=new ArrayList();
    }
    class Edge{
    	ArrayList<Integer> E=new ArrayList();
    }
     
    public class Main {
     
        final static int INF=0X3f3f3f3f;
        final static int maxn=100000;//开100000数组才过,我r
        static Vertex v[]=new Vertex[maxn+5];//v[i]存储与i相邻接的节点
        static Edge e[]=new Edge[maxn+5];//e[i]存储与i相邻接的边,与v[i]一一对应
        static boolean vis[]=new boolean[maxn+5];//防止重复访问
        static int dis[]=new int [maxn+5];//存储原始节点到各节点的dfs距离
        
        static void init(int n)//初始化
        {
        	for(int i=0;i<n;i++)
        	{
        		v[i]=new Vertex();
        		e[i]=new Edge();
        	}
        }
       
      
        static void dfs(int a)
        {
        	int len=v[a].V.size();
        	vis[a]=true;
        	for(int i=0;i<len;i++)//遍历邻接节点
        	{
        		int j=v[a].V.get(i);
        		if(!vis[j]&&e[a].E.get(i)!=INF)
        		{
        			
        			vis[j]=true;
        			dis[j]=dis[a]+e[a].E.get(i);
        			//System.out.println(a+" "+j+" "+dis[j]);
        			dfs(j);
        			vis[j]=false;//回溯
        		}
        	}
        }
       
        public static void main(String[] args) {
            
           Scanner cin = new Scanner(System.in);
           	int n=cin.nextInt();
           	
        	init(n);
        	for(int i=0;i<n-1;i++)
        	{
        		int a=cin.nextInt();
        		int b=cin.nextInt();
        		int d=cin.nextInt();
        		v[a-1].V.add(b-1);//节点从零开始
        		e[a-1].E.add(d);
        		v[b-1].V.add(a-1);
        		e[b-1].E.add(d);
        	}
        	Arrays.fill(vis,false);
        	Arrays.fill(dis,INF);
        	dis[0]=0;
        	dfs(0);//第一次遍历
        	long max=-1;
        	int temp=-1;
        	for(int i=0;i<n;i++)
        	{
        		if(dis[i]>max)
        		{
        			max=dis[i];
        			temp=i;
        		}
        	}
        	//System.out.println(temp);
        	
        	Arrays.fill(vis,false);
        	Arrays.fill(dis,INF);
        	dis[temp]=0;
        	dfs(temp);//第二次遍历
        	long ans=-1;//防止越界
    		for(int i=0;i<n;i++)
        	{
        		if(dis[i]>ans)
        		{
        			ans=dis[i];
        			temp=i;
        		}
        	}
    		//System.out.println(ans);
    		ans=ans*10+ans*(ans+1)/2;//如果ans是int的话,有可能越界
    		System.out.println(ans);
    		cin.close();
        }
     
    }
    
  • 相关阅读:
    Combine 框架,从0到1 —— 4.在 Combine 中使用计时器
    Combine 框架,从0到1 —— 4.在 Combine 中使用通知
    Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度
    Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
    使用 Swift Package Manager 集成依赖库
    iOS 高效灵活地配置可复用视图组件的主题
    构建个人博客网站(基于Python Flask)
    Swift dynamic关键字
    Swift @objcMembers
    仅用递归函数操作逆序一个栈(Swift 4)
  • 原文地址:https://www.cnblogs.com/a1439775520/p/12947508.html
Copyright © 2011-2022 走看看