zoukankan      html  css  js  c++  java
  • hdu3721: Building Roads(求树的中心,直径)


    hdu3721: Building Roads##

      Time Limit: 10 Sec
      Memory Limit: 128 MB

    Description###

       There is a magic planet in the space. There is a magical country on the planet. There are N cities in the country. The country is magical because there are exactly N-1 magic roads between the N cities, and from each city, it is possible to visit any other city. But after the huge expansion of the country, the road system seems to be messy. The moderator decided to rebuild the road system.
       As a worker, I cannot do too much things. I could just move one road to another position connecting arbitrary 2 cities using my magic, keeping its length unchanged. Of course, afterwards all the N cities have to be still connected. I wonder how to move in order to make the farthest distance between any two cities minimum. Could you solve it for me?
     

    Input###

       The first line of the input is one integer T (T ≤ 10), and then T test cases follow.
    Each test case begins with a line contains only one integer N (N ≤ 2500), means there are N magic cities. The cities are numbered from 0 to N-1.
    Following N-1 lines, each line has 3 integers a, b and c, means there is a magic road between a and b with distance c. (0 <= a, b < N, 0 < c <= 1000)
     

    Output###

       For each test case, output the case number and the corresponding minimum farthest distance. See sample for detailed format.
     

    Sample Input###

       2
       4
       0 1 2
       1 2 2
       2 3 2
       5
       0 1 1
       1 2 2
       2 3 3
       3 4 4
     

    Sample Output###

      Case 1: 4
      Case 2: 7
      

    HINT

    题目地址:hdu3721: Building Roads

    题目大意: 题目很简洁了:)

    题解:

      做法:
      枚举删哪一条边
      分成了两颗树
      求两颗树的中心连起来
      构成的树符合题目要求

      如何求树的中心
      1.从 i 出发向上,即终点不在以 i 为根的子树内的最长路径 up[i]
      2.从 i 出发向下,即终点在以 i 为根的子树内的最长路径 d1[i] 和次长路径 d2[i](c1[i],c2[i]记录从哪转移过来)
      首先,一便树形 dp 算出 d1,d2,c1,c2
         若d1[u]<d1[v]+e[i].val ——> d2[u]=d1[u],c2[u]=c1[u],
                       d1[u]=d1[v]+dis[u][v],c1[u]=v
      否则,若d2[u]<d1[v]+e[i].val ——> d2[u]=d1[v]+dis[u][v],c2[u]=v;

      d1,d2,c1,c2相对好求,如何求 up
      如果 fa[u] 的向下最长链不是从 v 过来的 ——> up[u]=max(d1[fa[u]],up[fa[u]])+dis[fa[u]][u]
      如果 fa[u] 的向下最长链是从 v 过来的  ——> up[u]=max(d2[fa[u]],up[fa[u]])+dis[fa[u]][u]

      画图看看
      
      如何在此基础上求树的直径
      我们只要遍历树上每一个点,直径是 (mx=max) { (d1[u]+d2[u]) } ((u in V))


    AC代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N=2505,inf=25e5+5;
    int Q,n;
    int U[N],V[N],W[N];
    bool vis[N];
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    struct edge{
    	int to,val,next;
    }e[N+N];
    int cnt_edge,last[N];
    bool flag[N+N];
    inline void add_edge(int u,int v,int w){
    	e[++cnt_edge]=(edge){v,w,last[u]};last[u]=cnt_edge;flag[cnt_edge]=1;
    	e[++cnt_edge]=(edge){u,w,last[v]};last[v]=cnt_edge;flag[cnt_edge]=1;
    }
    int d1[N],d2[N],c1[N];
    void dfs(int u,int fa){
    	d1[u]=d2[u]=0;									//赋值前清0
    	for(int i=last[u];i;i=e[i].next)
    		if(flag[i]){
    			int v=e[i].to;
    			if(v==fa)continue;
    			dfs(v,u);								//因为是从儿子更新自己先做儿子
    			if(d1[u]<d1[v]+e[i].val){				//发现从自己儿子到自己有更优解
    				d2[u]=d1[u];						//把最优解给次优
    				d1[u]=d1[v]+e[i].val;c1[u]=v;		//记一下最优解从哪里来(求 up[] 时有用)
    			}else if(d2[u]<d1[v]+e[i].val)
    				d2[u]=d1[v]+e[i].val;
    		}
    }
    int up[N];
    void _dfs(int u,int fa){
    	vis[u]=1;
    	for(int i=last[u];i;i=e[i].next)
    		if(flag[i]){
    			int v=e[i].to;
    			if(v==fa)continue;
    			if(c1[u]!=v)up[v]=max(d1[u],up[u])+e[i].val;	//看上解释
    			else up[v]=max(d2[u],up[u])+e[i].val;
    			_dfs(v,u);								//因为是自己更新儿子,先更新再 dfs
    		}
    }
    int main(){
    	Q=read();
    	for(int Case=1;Case<=Q;Case++){
    		n=read();
    		cnt_edge=0;
    		memset(last,0,sizeof(last));
    		for(int i=1;i<n;i++){
    			U[i]=read()+1,V[i]=read()+1,W[i]=read();//因为点的编号是 0..n-1 
    			add_edge(U[i],V[i],W[i]);
    		}
    		int res=inf;
    		for(int i=1;i<n;i++){
    			flag[i*2-1]=flag[i*2]=0;				//删边
    			dfs(U[i],0);							//求 d1[],d2[] (c1[],c2[])
    			dfs(V[i],0);
    			int dep1,dep2,mx=0;
    			dep1=dep2=inf;
    			memset(vis,0,sizeof(vis));
    			up[U[i]]=0;								//没有父亲 清0
    			_dfs(U[i],0);							//求一颗树的中心
    			for(int j=1;j<=n;j++)
    				if(vis[j] && max(d1[j],up[j])<dep1)	//求出一颗树的中心的最深dep1 (中心点编号 u1)
    					dep1=max(d1[j],up[j]);
    			memset(vis,0,sizeof(vis));
    			up[V[i]]=0;
    			_dfs(V[i],0);
    			for(int j=1;j<=n;j++)
    				if(vis[j] && max(d1[j],up[j])<dep2)	//求出另一颗树的中心的最深dep1 (中心点编号 u2)
    					dep2=max(d1[j],up[j]);
    			int ans=dep1+dep2+W[i];					//算出从新连边过的最长链
    			for(int j=1;j<=n;j++)
    			    mx=max(mx,d1[j]+d2[j]);				//两颗树最长的直径
    			ans=max(ans,mx);
    			res=min(res,ans);
    			flag[i*2-1]=flag[i*2]=1;				//把边加回来
    		}
    		printf("Case %d: %d
    ",Case,res);
    	}
    	return 0;
    }
    


      作者:skl_win
      出处:https://www.cnblogs.com/shaokele/
      本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    nagios安装配置
    Nagios:企业级系统监控方案
    使用Maven搭建Struts2+Spring3+Hibernate4的整合开发环境
    SecureCRT最佳配色方法+直接修改默认配置方法
    highcharts插件使用总结和开发中遇到的问题及解决办法
    关于Highcharts图表组件动态修改属性的方法(API)总结之Series
    Linux中环境变量文件及配置
    使用正则表达式匹配任意字符包括空格和换行符
    设置mysql远程连接root权限
    java读取文件夹下所有文件并替换文件每一行中指定的字符串
  • 原文地址:https://www.cnblogs.com/shaokele/p/9810602.html
Copyright © 2011-2022 走看看