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

题目大意:给你一棵有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. C获取本地时间的localtime函数

    最近有朋友问如下问题: #include <stdio.h>#include <stdlib.h>#include <iconv.h>#include <ti ...

  2. Spring boot将配置属性注入到bean类中

    一.@ConfigurationProperties注解的使用 看配置文件,我的是yaml格式的配置: // file application.yml my: servers: - dev.bar.c ...

  3. BZOJ1511: [POI2006]OKR-Periods of Words

    1511: [POI2006]OKR-Periods of Words Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 174  Solved: 92[Su ...

  4. 【转】在linux内核中读写文件 -- 不错

    原文网址:http://blog.csdn.net/tommy_wxie/article/details/8194276 1. 序曲 在用户态,读写文件可以通过read和write这两个系统调用来完成 ...

  5. 【转】两分钟彻底让你明白Android Activity生命周期(图文)!----不错

    原文网址:http://blog.csdn.net/android_tutor/article/details/5772285 大家好,今天给大家详解一下Android中Activity的生命周期,我 ...

  6. oracle事务和锁

    数据库事务概括 1. 说明 一组SQL,一个逻辑工作单位,执行时整体修改或者整体回退. 2.事务相关概念 1)事务的提交和回滚:COMMIT/ROLLBACK 2)事务的开始和结束 开始事务:连接到数 ...

  7. HDOJ 2131 Probability

    Problem Description Mickey is interested in probability recently. One day , he played a game which i ...

  8. Exception testing

    怎样去验证代码是否抛出我们期望的异常呢?虽然在代码正常结束时候验证很重要,但是在异常的情况下确保代码如我们希望的运行也很重要.比如说: new ArrayList<Object>().ge ...

  9. 首届京东自有品牌科技周“京东点亮生活”圆满成功 - 课程公告板 - 京东内部论坛 - Powered by Discuz!

    首届京东自有品牌科技周"京东点亮生活"圆满成功 - 课程公告板 - 京东内部论坛 - Powered by Discuz! 首届京东自有品牌科技周"京东点亮生活" ...

  10. uva 317 - Hexagon(规律推导)

    题目连接:317 - Hexagon 题目大意:在一个19个六边形组成的图形上玩一个游戏,给出9个数字, 分成3组, 分别可以填在左上角, 上, 有上角,因为对于小六边形来说, 对边的数是相同的, 然 ...