Uva1667NetworkMess——建树

题意:

有一颗n个叶子的无权树,输入两两叶子的距离,恢复出这棵树并输出每个非叶子节点的度数。

思路:

参考:https://blog.csdn.net/zju2016/article/details/78450509
无根树,现将一个叶子节点作为根。逐个探测剩下的叶子节点,并在这个过程中不断增加内部节点以满足其距离要求,最终建树完成。
Uva1667NetworkMess——建树_第1张图片
内部节点 j 的选择:
逐步测试是否能够找到某个内部的节点,使得该内部节点到所有的已经加入的外部节点的距离与待加入的外部节点的距离之差(tmp)相等,如果找到,就保存差值,逐步扩展并且计算扩展的点到已加入的外部节点的距离,计算其度数。
Uva1667NetworkMess——建树_第2张图片

#include 
#include 
#include 
#define fi first
#define se second
#define pii pair
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 2000+5;
int n, idx, dis[maxn][maxn], a[55][55], deg[maxn], id[55];

void Build(){
	id[1] = ++idx; dis[1][1] = 0;
	for(int i = 2; i <= n; ++i){
		int u, last, tmp;
		for(int j = 1; j <= idx; ++j){
			tmp = a[i][1] - dis[j][1];
			int fg = 0;
			for(int k = 2; k < i; ++k) if(a[i][k] - dis[j][id[k]] != tmp){fg = 1; break;} 
			if(!fg){last = j; break;}
		}
		for(int j = 1; j <= tmp; ++j,last = u){
			deg[u = ++idx] = 0; dis[u][u] = 0;
			++deg[last]; ++deg[u];
			for(int k = 1; k < idx; ++k) dis[u][k] = dis[k][u] = dis[k][last] + 1;
		}
		id[i] = idx;
		printf("idx = %d, ",idx);
	}puts("");
}

int main()
{
	freopen("in.txt","r",stdin);
	while(scanf("%d",&n) == 1&&n){
		for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) scanf("%d",&a[i][j]);
		idx = 0; memset(deg, 0, sizeof(deg));
		
		Build();
		
		sort(deg+1, deg+idx+1);
		for(int i = 1; i <= idx; ++i){
			if(deg[i] == 1) continue;
			printf("%d",deg[i]);
			if(i != idx) printf(" ");
		} 
		printf("\n");
	}
	return 0;
}

书上的思路是递归建树:

#include 
#include 
#include 
#include 
#include 
#define fi first
#define se second
#define pii pair
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 50+5;
const int maxsize = 2000+5;

vector M[maxn];

// 节点.nodes存的是以它为根的子树包含的叶子节点 
struct Node{ vector nodes; } Pool[maxsize];
typedef Node* PNode;
int cnt = 0;
PNode CreateNew(){ return &Pool[cnt++]; }
void Dispose(){ for(int i = 0; i < maxsize; ++i) Pool[i].nodes.clear(); }

// 并查集 
int fa[maxn];
int find_fa(int u){ return u == fa[u]? u : fa[u] = find_fa(fa[u]); }

// 叶子节点, 根到各叶子节点的距离,根节点指针,父节点指针,度 
void BuildTree(const vector& leaves, vector& dis, PNode root, PNode pa, vector& deg){
	// 初始化并查集 
	memset(fa, 0, sizeof(fa)); 
	for(int i = 0; i < leaves.size(); ++i) fa[leaves[i]] = leaves[i];
	
	int isLeaf[maxn]; 		// 直接在root下的叶子
	memset(isLeaf, 0, sizeof(isLeaf));
	
	// 遍历叶子 
	for(int i = 0; i < leaves.size(); ++i){
		int li = leaves[i];
		if(dis[li] == 1){
			isLeaf[li] = 1;
			root->nodes.push_back(CreateNew());		// 制造一个节点(不包含叶节点),便于后面计数 
			continue;
		}
		for(int j = i+1; j < leaves.size(); ++j){
			int ri = leaves[j];
			// 在同一个子树下, 归为一类 
			if(dis[li] + dis[ri] > M[li][ri]) fa[ri] = li;	
		}
	}
	for(int i = 0; i < leaves.size(); ++i) --dis[leaves[i]];	// 新的根到叶节点的距离
	map > subTrees;		// 每一个子树 
	for(int i = 0; i < leaves.size(); ++i){
		int x = leaves[i];
		if(0 == isLeaf[x]) subTrees[find_fa(x)].push_back(x);
	}
	
	// 递归处理每一个子树
	for(map >::iterator it = subTrees.begin(); it != subTrees.end(); ++it){
		root->nodes.push_back(CreateNew());		// 新的内部节点
		BuildTree(it->se, dis, root->nodes.back(), root, deg);
	} 
	
	if(pa&&!root->nodes.empty()) deg.push_back(root->nodes.size() + 1);
	
}

int main()
{
	freopen("in.txt","r",stdin);
	int n;
	while(scanf("%d",&n) == 1&&n){
		cnt = 0;
		for(int i = 0; i < n; ++i){
			M[i].clear();
			for(int j = 0; j < n; ++j){
				int a; scanf("%d",&a);
				M[i].push_back(a);
			}
		}
		vector leaves, dis(M[0].begin(), M[0].end()), deg;
		for(int i = 1; i < n; ++i) leaves.push_back(i);
		
		BuildTree(leaves, dis, CreateNew(), NULL, deg);
		
		sort(deg.begin(), deg.end());
		for(int i = 0; i < deg.size() - 1; ++i) printf("%d ",deg[i]);
		printf("%d\n", deg[deg.size()-1]);
		Dispose();
	}
	return 0;
}


你可能感兴趣的:(Practice,图论)