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

题目大意:给你一棵有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. poj 1364

    http://poj.org/problem?id=1364 #include<cstdio> #include<cstring> #include<algorithm& ...

  2. linxu安装OSX

    下载OS X Mavericks 10.9 Retail VMware Image安装vmware10.01打开unlock-all-v110,运行install.sh 打开vmx. 更新系统. 安装 ...

  3. Borg Maze(bfs+prim)

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6971   Accepted: 2345 Description The B ...

  4. POJ2253 Frogger(最短路)

    题目链接. 题意: 从0号点,到1号点,找一条能通过的路,使得这条路中的最大的边,比其它所有可能的路中的边都小. 分析: 这题就是按着dijkstra写,写着写着觉得像是prim了. 其中d[n]表示 ...

  5. 使用C#开发ActiveX控件 11

    C#开发ActiveX控件   ActiveX 是一个开放的集成平台,为开发人员.用户和 Web生产商提供了一个快速而简便的在 Internet 和 Intranet 创建程序集成和内容的方法. 使用 ...

  6. win7 清理系统

    1. 先用CCleaner等第三方软件清理一下.2. 将用户文件.我的文档.我的音乐.我的视频.桌面等路径更改到非系统分区(文件也会一并移动过去)3. 将系统盘的系统还原占用空间比减小,将大部分虚拟内 ...

  7. Java---StringBuffer()方法的简单应用

    描述:在实际应用中,经常回遇到对字符串进行动态修改.这时候,String类的功能受到限制,而StringBuffer类可以完成字符串的动态添加.插入和替换等操作. 1.构造函数.StringBuffe ...

  8. 学做酷炫有爱的免费网页,学习 Github Page 教你分分钟搭建自己的博客

    Github Page 网页搭建教程,教你分分钟搭建自己的博客 很多其它美丽的网页搭建教程教程.请看这里:http://www.duobei.com/course/8506331668 waterma ...

  9. 深入分析C++引用

    Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 关于引用和指针的差别的文章非常多非 ...

  10. android ListView和GridView拖拽移位具体实现及拓展

    关于ListView拖拽移动位置,想必大家并不陌生,比较不错的软件都用到如此功能了.如:搜狐,网易,百度等,但是相比来说还是百度的用户体验较好,不偏心了,下面看几个示例:              首 ...