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

题目大意:给你一棵有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. Neutron网络-OPENSTACK讲得比较透和方便

    http://www.ustack.com/blog/neutron_intro/ Neutron是OpenStack核心项目之一,提供云计算环境下的虚拟网络功能.Neutron的功能日益强大,并在H ...

  2. !!!全球最流行开源硬件平台!不知道就OUT了!

    全球最流行的几个开源硬件平台!不知道就OUT了! 随着物联网的推广和普及,五年内全球会有200亿台智能设备的需求,而如今随着创客概念的兴起,开源硬件也越加的火热,让我们来看看现在都有哪些主流的开源硬件 ...

  3. 【模拟】XMU 1062 山东煎饼

    题目链接: http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1062 题目大意: 已知金钱,和各面额钞票张数,问最少可以换成几张. 题目思路: [模拟 ...

  4. CSU 1111 有三户人家共拥有一座花园,每户人家的太太均需帮忙整理花园。A 太太工作了5 天,B 太太则工作了4 天,才将花园整理完毕。C 太太因为正身怀六甲无法加入她们的行动,所以就打算出90元钱

    题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82974#problem/D 解题思路:本题的意思就是三位均出90,然后AB按做 ...

  5. cf701C They Are Everywhere

    Sergei B., the young coach of Pokemons, has found the big house which consists of n flats ordered in ...

  6. DOL版USB Loader的下载和运行

    下载 在Wii上面玩硬盘版游戏,自然少不了USB Loader,相关教程和下载资源在网上一搜就有. 我在官网(地址:http://gwht.wikidot.com/usb-loader)上找到了一个下 ...

  7. Matlab:回归分析(2)

    方法一:直接用多元二项式回归 %数据输入 x1 = [ ]; x2 = [ ]; %多元二项式回归函数 y = [ ]; X = [x1' x2']; Y = y'; rstool(X, Y, 'pu ...

  8. FSharp.Data 程序集之 Http

    FSharp.Data 程序集之 Http (** # F# Data: HTTP Utilities .NET 库提供了强大的 API,产生和发送 HTTP WEB 请求,有两个类型,一个简单,`W ...

  9. MySQL主从复制(Master-Slave)与读写分离(MySQL-Proxy)

    MySQL主从复制(Master-Slave)即同步与读写分离(MySQL-Proxy)即集群

  10. java实现excel的导入导出(poi详解)[转]

    java实现excel的导入导出(poi详解) 博客分类: java技术 excel导出poijava  经过两天的研究,现在对excel导出有点心得了.我们使用的excel导出的jar包是poi这个 ...