感慨一下,区域赛的题目果然很费脑啊!!不过确实是一道不可多得的好题目!!

题目大意:给你一棵有n个节点的树,让你移动树中一条边的位置,即将这条边连接到任意两个顶点(边的大小不变),要求使得到的新树的直径最小。

解题思路:此题先求出原始树的直径maxr1,并记录直径上的各个节点。很容易想到要移动的边一定是直径上的边,只有这样才有可能使树的直径减小!! 接着就是枚举直径上的每条边,并用这条边作为分隔将原始树分割成两棵子树(即子树一和子树二),然后分别求子树一的直径maxr2 和子树二的直径maxr3。再找出子树一的直径的中点 和 子树二的直径的中点(这里的中点是指树中离树的直径的端点距离最小的点),将移动的边连接在这两个中点上,这样才能使生成的新树的直径sumtmp最小。最后求出min { maxr1 , maxr2 ,maxr3 ,sumtmp }即可。

Ps : 此题运用了很多技巧 ,如怎样找子树的中点?  生成两棵子树时是否要在图邻接表中删除此边 ?这些都有技巧,我这里找树的中点的算法的复杂度是O(n) ,具体详解请看代码:

// G++ 109ms AC
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std ;
int n ;
const int MAXN = 3000 ;
int path[MAXN] ; // 记录bfs路径
int shortest[MAXN] ; // 记录原始树的直径上的点
int shortmp2[MAXN] ; // 记录原始树拆分后的子树一的直径上的点
int shortmp3[MAXN] ; // 记录子树二的直径上的点
const int INF = 0x7fffffff ;
struct Node
{
int adj ;
int d ;
Node * next ;
};
Node * vert[MAXN] ;
int vis[MAXN] ;
int dis[5][MAXN] ;
int maxr[5];
queue<int> q ;
int bfs(int start , int xu) // 找树的直径
{
memset(dis[xu] , 0 , sizeof(dis[xu])) ;
maxr[xu] = 0 ;
while (!q.empty()) // 清空队列
{
q.pop() ;
}
int ans = start ; // ans 为直径的端点 ,注意,此处一定要把ans初始化为start !!
//因为当子树只有一个节点时,它的直
//径的两个端点均为start
vis[start] = 1 ;
dis[xu][start] = 0 ;
Node * p ;
int tmp ;
q.push(start) ;
while (!q.empty())
{
tmp = q.front() ;
vis[tmp] = 1 ;
q.pop() ;
p = vert[tmp] ;
while (p != NULL)
{
int tp2 = p -> adj ;
if(!vis[tp2])
{
vis[tp2] = 1 ;
if(dis[xu][tp2] < dis[xu][tmp] + p -> d)
{
dis[xu][tp2] = dis[xu][tmp] + p -> d ;
path[tp2] = tmp ;
}
if(maxr[xu] < dis[xu][tp2])
{
maxr[xu] = dis[xu][tp2] ;
ans = tp2 ;
}
q.push(tp2) ;
}
p = p -> next ;
}
}
return ans ;
}
int fz(int x , int y)
{
int sumtmp = 0 ;
memset(vis , 0 , sizeof(vis)) ;
memset(path , -1 , sizeof(path)) ;
vis[x] = vis[y] = 1 ; // 这是把树分割成两棵子树的技巧,不需
//把邻接表中的边(x , y) 删去,只需事
//先标记x和y即可
int dr2 , dl2 ;
dr2 = bfs(x , 2) ;
memset(vis , 0 , sizeof(vis)) ;
memset(path , -1 , sizeof(path)) ;
vis[y] = 1 ; // 注意这里 !!
dl2 = bfs(dr2 , 2) ;
int k = 0 ;
shortmp2[k] = dl2 ; // 记录子树一的直径上的点
while (path[shortmp2[k]] != -1)
{
k ++ ;
shortmp2[k] = path[shortmp2[k - 1]] ;
}
int ce2 ;
int maxt = INF ;
int j ;
for(j = 0 ; j <= k ; j ++) // 下面的过程为找子树一的直径的中点,比较重要
{
if(abs(maxr[2] - 2 * dis[2][shortmp2[j]]) < maxt)
{
maxt = abs(maxr[2] - 2 * dis[2][shortmp2[j]]) ;
ce2 = max(maxr[2] - dis[2][shortmp2[j]] , dis[2][shortmp2[j]]) ;
}
} // 下面是求子树二的直径和其直径的中点,方法与子树一相同
memset(vis , 0 , sizeof(vis)) ;
memset(path , -1 , sizeof(path)) ;
vis[x] = vis[y] = 1 ;
int dr3 , dl3 ;
dr3 = bfs(y , 3) ;
memset(vis , 0 , sizeof(vis)) ;
memset(path , -1 , sizeof(path)) ;
vis[x] = 1 ;
dl3 = bfs(dr3 , 3) ;
k = 0 ;
shortmp3[k] = dl3 ;
while (path[shortmp3[k]] != -1)
{
k ++ ;
shortmp3[k] = path[shortmp3[k - 1]] ;
}
maxt = INF ;
int ce3 ;
for(j = 0 ; j <= k ; j ++)
{
if(abs(maxr[3] - 2 * dis[3][shortmp3[j]]) < maxt)
{
maxt = abs(maxr[3] - 2 * dis[3][shortmp3[j]]) ;
ce3 = max(maxr[3] - dis[3][shortmp3[j]] , dis[3][shortmp3[j]]) ;
}
} // 以下是找出子树一、子树二和连接子树一和二得到的新树的直径的最大值
sumtmp = ce2 + ce3 + abs(dis[1][x] - dis[1][y]) ;
sumtmp = max(sumtmp , maxr[2]) ;
sumtmp = max(sumtmp , maxr[3]) ;
return sumtmp ;
}
void jie() // 求解本题
{
// 先求出原始树的直径以及直径上的点
memset(path , -1 , sizeof(path)) ;
memset(vis , 0 , sizeof(vis)) ;
int dr1 = bfs(0 , 1) ;
memset(vis , 0 , sizeof(vis)) ;
memset(path , -1 , sizeof(path)) ;
int dl1 = bfs(dr1 , 1) ;
int k = 0 ;
shortest[k] = dl1 ;
while (path[shortest[k]] != -1)
{
k ++ ;
shortest[k] = path[shortest[k - 1]] ;
}
int j ;
int maxans = maxr[1] ;
for( j = 0 ; j <= k ; j ++) // 枚举直径上的边,把原始树分割成两棵子树
{
int maxtmp = fz(shortest[j] ,shortest[j + 1]) ;
if(maxans > maxtmp)
{
maxans = maxtmp ;
}
}
printf("%d\n" , maxans) ;
}
void dele()
{
Node * p ;
int i ;
for(i = 0 ; i < n ; i ++)
{
p = vert[i] ;
while (p != NULL)
{
vert[i] = p -> next ;
delete p ;
p = vert[i] ;
}
}
}
int main()
{
int t ;
scanf("%d" , &t) ;
int cnt ;
for(cnt = 1 ; cnt <= t ; cnt ++)
{
memset(vert , 0 , sizeof(vert)) ;
scanf("%d" , &n) ;
int i ;
for(i = 1 ; i <= n - 1 ; i ++)
{
int a , b , c ;
scanf("%d%d%d" , &a , &b , &c) ; // 建图
Node * p ;
p = new Node ;
p -> adj = b ;
p -> d = c ;
p -> next = vert[a] ;
vert[a] = p ; p = new Node ;
p -> adj = a ;
p -> d = c ;
p -> next = vert[b] ;
vert[b] = p ;
}
printf("Case %d: " , cnt) ;
jie() ;
dele() ; //释放图
}
return 0 ;
}

HDU 3721 Building Roads (2010 Asia Tianjin Regional Contest) - from lanshui_Yang的更多相关文章

  1. HDU 3726 Graph and Queries(平衡二叉树)(2010 Asia Tianjin Regional Contest)

    Description You are given an undirected graph with N vertexes and M edges. Every vertex in this grap ...

  2. HDU-4432-Sum of divisors ( 2012 Asia Tianjin Regional Contest )

    Sum of divisors Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  3. hdu oj 3127 WHUgirls(2009 Asia Wuhan Regional Contest Online)

    WHUgirls Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total ...

  4. HDU 3695 / POJ 3987 Computer Virus on Planet Pandora(AC自动机)(2010 Asia Fuzhou Regional Contest)

    Description Aliens on planet Pandora also write computer programs like us. Their programs only consi ...

  5. HDU 3686 Traffic Real Time Query System(双连通分量缩点+LCA)(2010 Asia Hangzhou Regional Contest)

    Problem Description City C is really a nightmare of all drivers for its traffic jams. To solve the t ...

  6. HDU 3698 Let the light guide us(DP+线段树)(2010 Asia Fuzhou Regional Contest)

    Description Plain of despair was once an ancient battlefield where those brave spirits had rested in ...

  7. HDU 3685 Rotational Painting(多边形质心+凸包)(2010 Asia Hangzhou Regional Contest)

    Problem Description Josh Lyman is a gifted painter. One of his great works is a glass painting. He c ...

  8. HDU 4433 locker 2012 Asia Tianjin Regional Contest 减少国家DP

    意甲冠军:给定的长度可达1000数的顺序,图像password像锁.可以上下滑动,同时会0-9周期. 每个操作.最多三个数字连续操作.现在给出的起始序列和靶序列,获得操作的最小数量,从起始序列与靶序列 ...

  9. HDU 4436 str2int(后缀自动机)(2012 Asia Tianjin Regional Contest)

    Problem Description In this problem, you are given several strings that contain only digits from '0' ...

随机推荐

  1. Debug与Release有时候确实不一致

    不一致的原因不清楚. 情况1:耗了整整一天,也没查出Debug状况下错误的原因(3个库函数,用了1年多了,已经熟练使用,不会有问题的).到现在还是没搞明白为什么出问题. 情况2:还是上面的三个函数,D ...

  2. 【转】linux 原子整数操作详解

    原文网址:http://blog.csdn.net/hunanchenxingyu/article/details/8994379 printk(“%d\n”,atomic_read(&v)) ...

  3. bzoj3573[Hnoi2014]米特运输

    http://www.lydsy.com/JudgeOnline/problem.php?id=3573 好吧,虽然这是day1最后一题,但却是最水的一题....(前提:看懂题目) 仔细看题! 仔细看 ...

  4. Flask 安装 Ubuntu 14.04

    学习文档: http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world 中文版学习文档 开源中国版: ...

  5. J - Sabotage - UVA 10480(最大流)

    题目大意:旧政府有一个很庞大的网络系统,可以很方便的指挥他的城市,起义军为了减少伤亡所以决定破坏他们的网络,使他们的首都(1号城市)和最大的城市(2号城市)不能联系,不过破坏不同的网络所花费的代价是不 ...

  6. 10个提升iOS开发效率的必用工具

    Xcode插件 几乎所有开发者都知道Alcatraz是一个开源的包管理工具,可以让我们更轻松地管理各种插件.接下来就介绍下我的最推荐的10个插件: 10.HOStringSense 在编辑字符串的时候 ...

  7. java入门之异常处理小结

    (1)异常和错误 异常(Exception):不正常的事件,会中断程序,在运行中发生的意外,程序本身可以处理,防止程序中断:程序中会捕获的异常信息,并告诫用程序员,不要求程序必须对它做处理.catch ...

  8. Android 开发经验

    学习社区 eoe移动开发者社区 (link) 链接:http://www.eoeandroid.com/ 环境配置 Cocos2d-x 3.x 全平台新手开发配置教程 链接:http://www.co ...

  9. Windows 10 TH2

    Windows 10 TH2到底更新了啥? 15年11月,微软正式向Windows 10用户推送了Threshold 2(简称TH2)更新, 也就是传说中的November Update.更新后系统版 ...

  10. Oracle Quality --- Setup Collection Element and Collection Plan

    Responsibility: Quality, Vision Enterprises 第一步: 创建 Collection Elements setup > collection elemen ...