Aizu 1263 Network Mess 树的直径+构造

题目链接:点击打开链接

Network Mess

Gilbert is the network admin of Ginkgo company. His boss is mad about the messy network cables on the floor. He finally walked up to Gilbert and asked the lazy network admin to illustrate how computers and switches are connected. Since he is a programmer, he
is very reluctant to move throughout the office and examine cables and switches with his eyes. He instead opted to get this job done by measurement and a little bit of mathematical thinking, sitting down in front of his computer all the time. Your job is to
help him by writing a program to reconstruct the network topology from measurements.

There are a known number of computers and an unknown number of switches. Each computer is connected to one of the switches via a cable and to nothing else. Specifically, a computer is never connected to another computer directly, or never connected to two or
more switches. Switches are connected via cables to form a tree (a connected undirected graph with no cycles). No switches are ‘useless.’ In other words, each switch is on the path between at least one pair of computers.

All in all, computers and switches together form a tree whose leaves are computers and whose internal nodes switches (See Figure 9).

Gilbert measures the distances between all pairs of computers. The distance between two com- puters is simply the number of switches on the path between the two,
plus one. Or equivalently, it is the number of cables used to connect them. You may wonder how Gilbert can actually obtain these distances solely based on measurement. Well, he can do so by a very sophisticated statistical processing technique he invented.
Please do not ask the details.

You are therefore given a matrix describing distances between leaves of a tree. Your job is to construct the tree from it.

Input

The input is a series of distance matrices, followed by a line consisting of a single ‘0‘. Each distance matrix is formatted as follows.

N
a11 a12  ...   a1N
a21 a22  ...   a2N
 .   .  .      .
 .   .    .    .
 .   .      .  .
aN1 aN2  ...   aNN

N is the size, i.e. the number of rows and the number of columns, of the matrix. aij gives
the distance between the i-th leaf node (computer) and the j-th. You
may assume 2 ≤ N ≤ 50 and the matrix is symmetric whose diagonal elements are all zeros. That is, aii =
0 and aij = aji for
each i and j. Each non-diagonal element aij (i ≠j)
satisfies 2 ≤ aij ≤
30. You may assume there is always a solution. That is, there is a tree having the given distances between leaf nodes.

Output

For each distance matrix, find a tree having the given distances between leaf nodes. Then output the degree of each internal node (i.e. the number of cables adjoining each switch), all in a single line and in ascending order. Numbers in a line should be separated
by a single space. A line should not contain any other characters, including trailing spaces.

Sample Input

4
   0  2  2  2
   2  0  2  2
   2  2  0  2
   2  2  2  0
4
   0  2  4  4
   2  0  4  4
   4  4  0  2
   4  4  2  0
2
   0 12
  12  0
0

Output for the Sample Input

4
2 3 3
2 2 2 2 2 2 2 2 2 2 2


Source: ACM International Collegiate Programming Contest , Asia Regional Tokyo, Tokyo, Japan, 2005-11-04

http://www2.teu.ac.jp/icpc/

题意:

给出n表示有n台电脑,两两之间通过路由器连接(电脑是不能直接相连的)

再给出n*n的邻接矩阵表示任意两台电脑间的距离。

在局域网里加入树形的路由器使得满足给出的邻接矩阵。

输出每个路由器 连接的网线根数

思路:

首先电脑-路由器的距离恒为1,也就是说任意两台电脑间都>=2,所以去掉 (电脑-路由)这样的网线,所以给输入的矩阵-=2

这样我们就把电脑移动到了路由器上。

observe :

对于路由器构成的树,叶子节点上一定有电脑(当然电脑可能不只在叶子节点上)

所以一定存在2台电脑,使得这两台电脑在叶子节点上且这2个叶子节点间的路径是树的直径。所以找出这2台电脑

in a word, 找2个距离最远的节点。距离记为maxlen;

然后我们构造长度为maxlen的长链。

我们把第i台电脑加入这个树,则我们要找2个点满足:

1、这2个点已经加入了树

2、i点到这两个点形成的链的距离最短。

如此把n个节点加入树后再遍历一下树就能得到答案。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if(c=getchar(),c==EOF) return 0;
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
    if (x <0) {
        putchar('-');
        x = -x;
    }
    if(x>9) pt(x/10);
    putchar(x%10+'0');
}
using namespace std;
const int inf = (int)1e8;
typedef pair<int,int> pii;
const int N = 1050;
struct Edge{
	int from, to, nex;
}edge[1000000];
int head[N], edgenum, tot, <span class="KSFIND_CLASS_SELECT" id="0KSFindDIV">dd</span>[N]; //dd[i]表示树中的第i个点连了几台电脑,因为du[i]表示的是树边,所以i点的网线数=du[i]+dd[i]
void a<span class="KSFIND_CLASS" id="1KSFindDIV">dd</span>(int u, int v){
	Edge E = {u, v, head[u]};
	edge[edgenum] = E;
	head[u] = edgenum++;
}
void init(){
	memset(head, -1, sizeof head); tot = 1; edgenum = 0;
}
void creat(int point, int len){
	a<span class="KSFIND_CLASS" id="2KSFindDIV">dd</span>(point, ++tot);
	a<span class="KSFIND_CLASS" id="3KSFindDIV">dd</span>(tot, point);
	if(len) creat(tot, len-1);
}
int D[N];
vector<int> bfs(int root, int len){ //寻找树上距离root距离为len的,返回这样的点的集合
//	printf("Bfs:root=%d, dis=%d\n", root, len);
	for(int i = 1; i <= tot; i++)D[i] = -1;
	D[root] = 0;
	queue<int>q; q.push(root);
	vector<int>ans;
	while(!q.empty()){
		int u = q.front(); q.pop();
		if(D[u] == len){
			ans.push_back(u); continue;
		}
		for(int i = head[u]; ~i; i = edge[i].nex){
			int v = edge[i].to;
			if(D[v] == -1){
				q.push(v); D[v] = D[u]+1;
			}
		}
	}
	return ans;
}
int Jiao(vector<int>X, vector<int>Y){//找一个点ans使得ans同时存在X集合和Y集合(也就是找XY集合的交集)
	for(int i = 0; i < X.size(); i++)
		if(find(Y.begin(), Y.end(), X[i])!=Y.end())return X[i];
}
int n, dis[55][55], maxlen;
int vis[55];//vis[i]表示第i台电脑在树中的编号是几(vis[i]=0表示这个点尚未加入树
pii root;//root就是树的直径的两端
void input(){
	init();
	maxlen = -1;
	memset(D, -1, sizeof D);
	memset(<span class="KSFIND_CLASS" id="4KSFindDIV">dd</span>, 0, sizeof <span class="KSFIND_CLASS" id="5KSFindDIV">dd</span>);
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++){
			rd(dis[i][j]);
			if(i == j)continue;
			dis[i][j] -= 2;
			if(dis[i][j]>maxlen){
				maxlen = dis[i][j];
				root = pii(i, j);
			}
		}
}
int du[N];
vector<int>ans;
void Find_ans(int u, int fa){
	du[u] = 0;
	for(int i = head[u]; ~i; i = edge[i].nex){
		int v = edge[i].to; du[u]++;
		if(v!=fa)Find_ans(v, u);
	}
	ans.push_back(du[u]+<span class="KSFIND_CLASS" id="6KSFindDIV">dd</span>[u]);
}
void put_tree(){
	for(int i = 1; i <= tot; i++){
		printf("%d:", i);
		for(int j = head[i]; ~j; j = edge[j].nex)printf(" %d", edge[j].to);
		puts("");
	}
}
int main(){
	while(~scanf("%d", &n), n){
		input();
		if(maxlen == 0){printf("%d\n", n); continue;}
		memset(vis, 0, sizeof vis);
		vis[root.first] = tot;
		creat(vis[root.first], maxlen-1);
		vis[root.second] = tot;
		<span class="KSFIND_CLASS" id="7KSFindDIV">dd</span>[vis[root.first]]++; <span class="KSFIND_CLASS" id="8KSFindDIV">dd</span>[vis[root.second]]++;
	//	printf("最长链:(%d,%d), 长度:%d, 最长链在树中对应的点标:(%d,%d)\n", root.first, root.second, maxlen, vis[root.first], vis[root.second]);
		for(int k = 1; k <= n; k++){
			if(vis[k])continue;
			int cha = inf;
			pii hehe;
			for(int i = 1; i <= n; i++)
				for(int j = 1; j <= n; j++)
				if(i!=j && vis[i] && vis[j])
				{
					int C = dis[i][k] + dis[k][j] - dis[i][j];
					if(cha > C) cha = C, hehe = pii(i, j);
				}
				int tmp = (dis[hehe.first][k] + dis[hehe.second][k] - dis[hehe.first][hehe.second]) / 2;
	//		printf("%d点找到的链是(%d,%d) %d,%d %d tmp=%d\n", k,hehe.first, hehe.second, dis[hehe.first][k], dis[hehe.second][k], dis[hehe.first][hehe.second], tmp);
			int treepoint = Jiao( bfs(vis[hehe.first], dis[hehe.first][k] - tmp), bfs(vis[hehe.second], dis[hehe.second][k] - tmp));
		//	printf("%d点的支链长度为%d 分支点:%d\n",k, tmp, treepoint);
			if(tmp == 0){
				<span class="KSFIND_CLASS" id="9KSFindDIV">dd</span>[treepoint] ++; vis[k] = treepoint; continue;
			}
			creat(treepoint, tmp-1);
			<span class="KSFIND_CLASS" id="10KSFindDIV">dd</span>[tot]++;
			vis[k] = tot;
		//	printf("%d在树中对应的点标:%d\n", k, vis[k]);
	//	put_tree();		puts("");
		}
		ans.clear();
		Find_ans(root.first, -1);
		sort(ans.begin(), ans.end());
		for(int i = 0; i < ans.size() ; i++)
			printf("%d%c", ans[i], i+1==(int)ans.size()?'\n':' ');
	}
	return 0;
}
/*
2
0 4
4 0
6
0 6 6 6 7 7
6 0 6 6 9 8
6 6 0 4 9 8
6 6 4 0 9 8
7 9 9 9 0 5
7 8 8 8 5 0

*/

dd

时间: 2024-10-29 10:45:49

Aizu 1263 Network Mess 树的直径+构造的相关文章

codeforces GYM 100114 J. Computer Network tarjan 树的直径 缩点

J. Computer Network Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description The computer network of “Plunder & Flee Inc.” consists of n servers and m two-way communication links. Two servers can communicate either thr

codeforces GYM 100114 J. Computer Network 无相图缩点+树的直径

题目链接: http://codeforces.com/gym/100114 Description The computer network of “Plunder & Flee Inc.” consists of n servers and m two-way communication links. Two servers can communicate either through a direct link, or through a chain of links, by relayi

poj 1985 Cow Marathon 【树的直径】

题目:poj 1985 Cow Marathon 题意:给出一个树,让你求树的直径. 分析: 树的直径:树上两点之间的最大距离. 我们从任意一点出发,BFS一个最远距离,然后从这个点出发,在BFS一个最远距离,就是树的直径. AC代码: /* POJ:1985 Cow Marathon 2014/10/12/21:18 Yougth*/ #include <cstdio> #include <iostream> #include <algorithm> #include

poj1849(求树的直径)

题目链接:http://poj.org/problem?id=1849 题意:有一颗n个结点的带权的无向树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 但是最后机器人不要求回到出发点. 问你两个机器人走的路总长之和的最小值是多少? 分析:如果从某点出发遍历完一棵树再回来,那么所有边都会走两遍,而从某点有两个机器人出发去遍历,因为不用回来,所以最后那两个人距离越远越好,可以从树的直径上某个点背道而驰,那么这段距离(树的直径)只走了一遍,其他的要走两遍,所以ans=sum*2-l

SDUT OJ 3045 迷之图论 (树的直径)

题目地址:SDUT OJ 3045 这题比赛的时候想的差不多..但是总是觉得不对..写了一次就没再写,然后删了..当时没想到的是第二次求出来的就是最长链..当时想到的两次bfs找最大值(这一种方法其实结果也对..TAT..),还有找到点后在回溯减去重点等等..但总觉得好像都不太对...赛后才知道这题原来是树的直径.....牡丹江区域现场赛的时候遇到过,不过赛后也没看... 找树的直径的方法其实就是先任取一点进行bfs,找到最远的一点,这时最远的一点肯定是最长链端点之一,然后再从这一最远点开始bf

hdu4612 无向图中任意添加一条边后使桥的数量最少 / 无向图缩点+求树的直径

题意如上,含有重边(重边的话,俩个点就可以构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选任意起点U,进行bfs,到达最远的一个点v(level最深)该点必然是树的直径的一个端点,,再从该点出发,bfs,到最深的一点,该点深度就是直径.(证明:先假设u,是直径上一点,S,T是直径的端点,设v!=t,则有(V,U)+(U,S)>(T,U)+(U,S),矛盾,故t=v:若u不是直径上一点,设u到直径上的一点为x,同理易证. 最后 缩

ZOJ Problem Set - 3820 Building Fire Stations 【树的直径 + 操作 】

题目:problemId=5374" target="_blank">ZOJ Problem Set - 3820 Building Fire Stations 题意:给出n个点,n-1条边的一棵树.然后要在两个点上建立两个消防站.让全部点的到消防站最大距离的点的这个距离最小. 分析:首先先求这个树的直径.然后在树的直径的中点处把树分成两棵树.然后在把两棵树分别取中点的最大值就是ans值. 这个题目数据有点水了感觉... AC代码: #include <cstdi

BDFZOI 树的直径

提交次数:2 涉及知识:基础图论/BFS 描述 一棵树T的"直径"定义为结点两两间距离的最大值.给定带权树T,求T的直径长度. 输入 第一行包含2个整数N.M,表示图中共有N个结点和M条无向边.(N <= 5000,M<n)接下来M行,每行包含3个整数{u,v,w},表示有一条无向边连接结点u.v*输入保证是无环图输出一个整数,代表直径长度 样例输入 4 31 2 12 3 22 4 3 样例输出 5 代码: 1 #include<iostream> 2 #in

hdu 4607(树的直径)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4607 题解:给定一棵树,从树中的任意选一个顶点出发,遍历K个点的最短距离是多少?(每条边的长度为1) 算法分析: 首先如果k小于等于直径长度,那么答案为k−1;如果k大于直径长度,设直径长度为r,那么答案为r−1+(k−r)*2;树的直径:树上的最长简单路径; 代码: 1 #include <cstdio> 2 #include <cmath> 3 #include <cstri