前段时间的网赛有一道题目是最小树形图,感觉这玩意儿挺有用的,于是准备折腾下。最小树形图比较麻烦,加上这段时间各种事情外加不在状态,这玩意儿一折腾就是1个多礼拜……终于算是折腾出来了。

最小树形图的基本思路还算容易理解,百度百科里面写的挺详细的,大概就是找入边、找环、缩点的过程,不过具体实现的时候遇到的问题还是比较多,一是缩点具体如何实现,二是如何输出路径。POJ3164不需要输出路径,只需要输出结果,于是我一开始决定把这玩意儿过了。

为了增加通用性,我是用邻接表+STL写的,具体实现的时候开一个merge数组记录某个点是否已经被合并了,如果没有被合并merge数组内容就是-1,如果被合并了就是合并后所在的那个节点的标号,pre数组记录当前找到的一堆入边所对应的前缀,len数组记录当前找到的一堆入边的长度

接着就是比较朴素的一坨循环,先是一个二重循环找到最短入边,再一个dfs找环,如果找到环了就把环缩成一个点,具体缩点的时候是新开一个点,然后按照定义把被缩的点的出边都加到新开的那个点里面,然后把被缩的点的入边的长度及指向改了- - (也可以直接添加一条心边,不过改的话速度要快一些,在HDU2121里面添加要TLE,而改不TLE),完了之后就是下一次循环了。

HDU那个需要确定根,具体实现的时候是通过加一个超级根。但是输出根的时候需要知道整个树的构造(其实有其他办法,不过如果能够输出树的构造那显然通用很多),我YY到一个办法,对于每一条边,记录下org_u, org_v,表示这条边最初的起点跟终点。然后开一个uu跟vv数组,之后在寻找入边的过程中,更新一条边就把uu,vv也刷新一下,最后根据uu跟vv就能够得到整棵树的构造。

贴代码……

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath.>

using namespace std;

struct CNode {
	int v;
	double l;

	CNode(int v, double l)
	{
		this->v = v;
		this->l = l;
	}
};

const int MAXN = 2010;

vector<CNode> a[MAXN];

int n, m, nn; //nn表示添加扩展点之后的点上界

int pre[MAXN], flag[MAXN], merge[MAXN];
double len[MAXN];

double sum;

void dfs1(int u)
{
	if (flag[u] > 0)
		return;
	flag[u] = 1;
	for (vector<CNode>::iterator i = a[u].begin(); i != a[u].end(); ++i) {
		dfs1(i->v);
	}
}

bool Check(int root)
{
	memset(flag, 0, sizeof (flag));
	dfs1(root);
	for (int i = 0; i < n; ++i)
		if (!flag[i])
			return false;
	return true;
}
//dfs2,递归找环,如果找到就返回环中第一个点的标号,如果未找到就返回-1
//flag=0表示未发现,=1表示在栈里面,=-1表示已经处理过

int dfs2(int u)
{

	if (u < 0 || flag[u] < 0)
		return -1;
	if (flag[u] > 0)
		return u;
	flag[u] = 1;
	int ret = dfs2(pre[u]);
	flag[u] = -1;
	return ret;
}
//最小树形图,朱刘算法
//返回最小树形图的边长和,如果无解返回-1

double ZhuLiu(int root)
{
	if (!Check(root))
		return -1;
	double ans = 0;
	for (int i = 0; i < 2 * n; ++i) {
		pre[i] = -1;
		len[i] = 0;
		merge[i] = i;
	}
	nn = n;
	while (true) {
		for (int i = 0; i < nn; ++i) {
			pre[i] = -1;
		}
		for (int i = 0; i < nn; ++i) {
			if (merge[i] != i)
				continue;
			for (vector<CNode>::iterator index = a[i].begin(); index != a[i].end(); ++index) {
				if (merge[index->v] != index->v)
					continue;
				if (pre[index->v] == -1 || len[index->v] > index->l) {
					pre[index->v] = i;
					len[index->v] = index->l;
				}
			}
		}
		bool noLoop = true;
		//找环
		for (int i = 0; i < nn; ++i) {
			flag[i] = 0;
		}
		pre[root] = -1;
		for (int u = 0; u < nn; ++u) {
			int t = dfs2(u);
			if (t < 0)
				continue;
			noLoop = false;
			//发现一个环,缩成点
			int now = t;
			do {
				merge[now] = nn;
				now = pre[now];
				ans += len[now];
			} while (now != t);
			//处理边
			a[nn].clear();
			for (int i = 0; i < nn; ++i) {
				if (merge[i] == nn) {
					for (vector<CNode>::iterator index = a[i].begin(); index != a[i].end(); ++index) {
						if (merge[index->v] == index->v)
							a[nn].push_back(CNode(index->v, index->l));
					}
				} else if (merge[i] == i) {
					int k = a[i].size();
					for (int j = 0; j<k; ++j) {
						if (merge[a[i][j].v] == nn){
							//a[i].push_back(CNode(nn, a[i][j].l - len[a[i][j].v]));
							a[i][j].l -= len[a[i][j].v];
							a[i][j].v = nn;
						}
					}
				}
			}
			nn++;
		}
		if (noLoop) {
			for (int i = 0; i < nn; ++i) {
				if (merge[i] == i && i != root){
					ans += len[i];
				}
			}
			for(int i = 0; i<nn; ++i)
				pre[vv[i]] = uu[i];
			pre[root] = -1;
			return ans;
		}
	}
}

double x[MAXN], y[MAXN];

double sqr(double k){
	return k*k;
}

bool Solve()
{
	if (EOF == scanf("%d%d", &n, &m))
		return false;

	for (int i = 0; i < n; ++i)
		a[i].clear();
	for(int i = 0; i<n; ++i){
		scanf("%lf%lf", &x[i], &y[i]);
	}
	for (int i = 0; i < m; ++i) {
		int u, v;
		scanf("%d%d", &u, &v);
		u--;
		v--;
		if (u == v)
			continue;
		a[u].push_back(CNode(v, sqrt(sqr(x[u]-x[v])+sqr(y[u]-y[v]))));
	}
	double ret = ZhuLiu(0);
	if (-1 == ret) {
		printf("poor snoopy\n");
	} else {
		printf("%.2f\n", ret);
	}
	return true;
}

int main(int argc, char** argv)
{
//	freopen("in.txt", "r", stdin);
	while (Solve());
	return 0;
}




#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>

using namespace std;

struct CNode {
	short v, org_u, org_v;
	double l;

	CNode(short v, double l, short org_u, short org_v)
	{
		this->v = v;
		this->l = l;
		this->org_u = org_u;
		this->org_v = org_v;
	}
};

const int MAXN = 2010;

vector<CNode> a[MAXN];

int n, m, nn; //nn表示添加扩展点之后的点上界

int pre[MAXN], flag[MAXN], merge[MAXN], uu[MAXN], vv[MAXN];
double len[MAXN];

double sum;

void dfs1(int u)
{
	if (flag[u] > 0)
		return;
	flag[u] = 1;
	for (vector<CNode>::iterator i = a[u].begin(); i != a[u].end(); ++i) {
		dfs1(i->v);
	}
}

bool Check(int root)
{
	memset(flag, 0, sizeof (flag));
	dfs1(root);
	for (int i = 0; i < n; ++i)
		if (!flag[i])
			return false;
	return true;
}
//dfs2,递归找环,如果找到就返回环中第一个点的标号,如果未找到就返回-1
//flag=0表示未发现,=1表示在栈里面,=-1表示已经处理过

int dfs2(int u)
{

	if (u < 0 || flag[u] < 0)
		return -1;
	if (flag[u] > 0)
		return u;
	flag[u] = 1;
	int ret = dfs2(pre[u]);
	flag[u] = -1;
	return ret;
}
//最小树形图,朱刘算法
//返回最小树形图的边长和,如果无解返回-1

double ZhuLiu(int root)
{
	if (!Check(root))
		return -1;
	double ans = 0;
	for (int i = 0; i < 2 * n; ++i) {
		pre[i] = -1;
		len[i] = 0;
		merge[i] = i;
		uu[i] = -1;
	}
	nn = n;
	while (true) {
		for (int i = 0; i < nn; ++i) {
			pre[i] = -1;
		}
		for (int i = 0; i < nn; ++i) {
			if (merge[i] != i)
				continue;
			for (vector<CNode>::iterator index = a[i].begin(); index != a[i].end(); ++index) {
				if (merge[index->v] != index->v)
					continue;
				if (pre[index->v] == -1 || len[index->v] > index->l) {
					pre[index->v] = i;
					len[index->v] = index->l;
					uu[index->v] = index->org_u;
					vv[index->v] = index->org_v;
				}
			}
		}
		bool noLoop = true;
		//找环
		for (int i = 0; i < nn; ++i) {
			flag[i] = 0;
		}
		pre[root] = -1;
		for (int u = 0; u < nn; ++u) {
			int t = dfs2(u);
			if (t < 0)
				continue;
			noLoop = false;
			//发现一个环,缩成点
			int now = t;
			do {
				merge[now] = nn;
				now = pre[now];
				ans += len[now];
			} while (now != t);
			//处理边
			a[nn].clear();
			for (int i = 0; i < nn; ++i) {
				if (merge[i] == nn) {
					for (vector<CNode>::iterator index = a[i].begin(); index != a[i].end(); ++index) {
						if (merge[index->v] == index->v)
							a[nn].push_back(CNode(index->v, index->l, index->org_u, index->org_v));
					}
				} else if (merge[i] == i) {
					int k = a[i].size();
					for (int j = 0; j<k; ++j) {
						if (merge[a[i][j].v] == nn){
							a[i][j].l -= len[a[i][j].v];
							a[i][j].v = nn;
						}
					}
				}
			}
			nn++;
		}
		if (noLoop) {
			for (int i = 0; i < nn; ++i) {
				if (merge[i] == i)
					ans += len[i];
			}
			for(int i = 0; i<nn; ++i){
				if(i != root)
					pre[vv[i]] = uu[i];
			}
			return ans;
		}
	}
}

bool Solve()
{
	if (EOF == scanf("%d%d", &n, &m))
		return false;
	for (int i = 0; i < MAXN; ++i)
		a[i] = vector<CNode>();
	sum = 0;
	for (int i = 0; i < m; ++i) {
		int u, v, l;
		scanf("%d%d%d", &u, &v, &l);
		if (u == v)
			continue;
		a[u].push_back(CNode(v, l, u, v));
		sum += l;
	}
	sum++;
	for (int i = 0; i < n; ++i)
		a[n].push_back(CNode(i, sum, n, i));
	n++;
	long long ret = ZhuLiu(n - 1);
	if (-1 == ret || ret >= 2*sum) {
		printf("impossible\n\n");
	} else {
		int root = -1;
		for(int i = 0; i<n-1; ++i)
			if(pre[i] == n-1){
				root = i;
			}
		printf("%.0f %d\n\n", (double)(ret - sum), root);
	}
	return true;
}

int main(int argc, char** argv)
{
//	freopen("in.txt", "r", stdin);
	while (Solve());
	return 0;
}