BZOJ 2657 (ZJOI 2012 旅游) 求树上最长链(树的直径) MAP建树+BFS/DFS

来源:互联网 发布:詹姆斯本赛季数据 编辑:程序博客网 时间:2024/06/06 08:41

这道题是挺简单,求树上一条最长的路径(后来才知道这叫树的直径),而麻烦的是如何建树,这是这道题略恶心的地方。


数据的给出方式是每个节点给3个值(1~n),如果两个节点间有两个值相同,那么这两个点就是有一条边相连。题目中的描述什么凸多边形,三角形的顶点什么的一开始看确实被吓到了,其实就是两个三角形如果有公共边,那么这三角形代表的节点之间就有一条边,三角形的三个顶点就是前面所说的三个值。又因为整个图是凸多边形,也就是说三角形的顶点不会在内部出现,那么就是说不会有环的存在,而且整个图是一个连通的,又因为一共有n个顶点,会形成n-2个三角形,也就是说有n-2个节点,每个节点的三个值都是1~n之间的数,所以可以推得一共有n-3条边。n-2个节点,n-3条边,无环,连通,所以这是棵树。


那么我们怎么建树呢?——通过已有的每个节点的3个值。因为树边是需要两个值相等,所以我们需要记录两个值,如现在输入一个节点的三个值a,b,c,我们需要判断(a,b), (b,c), (a,c)是否存在,如果存在,就把存在的那个节点与当前结点连边,不存在则记录为存在。因为输入的三个值是无序的,而且边的存在性也不需要两个值顺序固定,所以我们要把a,b,c排序,避免明明存在(a,c)却因为当前输入顺序是(c,a)而判定为不存在。那么记录(a,b)这样的一个数对存在用二维数组会爆内存,不过我们可以想到STL里的MAP。定义一个<pair<int,int>, int>型的map,pair是数对,int是这个数对存在的节点,因为边是以相邻的三角形的公共边产生的,所以不用担心一个数对会出现3次或3次以上。


有了树,求直径:先以任意一个点为根BFS/DFS,找出距离最远的点,再以那个距离最远的点为根BFS/DFS,这次再求出来最远的点,这两个点的距离就是所求的最长路径了。BFS/DFS很好写,相当于裸题了。


#include <cstdio>#include <algorithm>#include <cstring>#include <map>#include <cstdlib>#include <vector>#define M 200002using namespace std;int n, p, d;vector <int> G[M];map <pair<int, int>, int> m;pair <int, int> t;void init(){int a[3];scanf("%d", &n);for(int i = 1; i < n-1; i++){scanf("%d %d %d", a, a+1, a+2);sort(a, a+3);t = make_pair(a[0], a[1]);if(m[t]) G[i].push_back(m[t]), G[m[t]].push_back(i);else m[t] = i;t = make_pair(a[0], a[2]);if(m[t]) G[i].push_back(m[t]), G[m[t]].push_back(i);else m[t] = i;t = make_pair(a[1], a[2]);if(m[t]) G[i].push_back(m[t]), G[m[t]].push_back(i);else m[t] = i;  }}void dfs(int u, int fa, int dis){if(dis > d) p = u, d = dis;for(int i = 0; i < G[u].size(); i++){int v = G[u][i];if(v == fa) continue;dfs(v, u, dis+1);}}int main(){init();dfs(1, 0, 1);dfs(p, 0, 1);printf("%d", d);}

0 0
原创粉丝点击