最小树形图

最小树形定义:在向图中,通过某一个定点v,能访问到所有其它点,且使权值和最小。

本文介绍的算法叫朱刘算法,是由两位中国人朱永津,刘振宏在1965所发明。算法的核心思想是除了定点外,每个点必有一条入边,贪心及缩点。它的步骤是:

step1:从v点出发,检测是否能访问所有顶点。能,执行step2;否则不存在最小树形图,返回。

step2:对于除定点v之外的所有点,找出它的最小入边pre[i]及其权值in[i];

step3:对于step2找出的所有边,检测是否存在环,不存在,则最小权值和为sum{in[i]},1 <= i <= vertex(点的总和),返回;否则,执行step4;

step4:将环上的点缩成一个新点,用这个新点代替原来环上所有的点。而对于所有的点i,修改其入边值costi = costi - in[i];返回step2。

模板是根据hh的改的http://www.notonlysuccess.com/index.php/mst/

struct Graph
{
	#define N 101
	#define M 10001
	#define INF 100000000

	int vertex,edge;
	int head[N];
	struct Node
	{
		//u --> v
		int u;
		int v;
		int next;
		int cost;
		Node(){};
		Node(int a,int b,int c,int d):u(a),v(b),next(c),cost(d){};
	}adjlist[M];

	//valuables min_tree needs
	int visited[N];
	int in[N],pre[N],color[N];
	int mincost,count;

	void init(int n)
	{
		memset(head,-1,sizeof(head));
		vertex = n;
		edge = 0;
	}

	void insert(int u,int v,int w)
	{
		adjlist[edge] = Node(u,v,head[u],w);
		head[u] = edge++;
	}

	int min_tree(int root)
	{
		//检测从根能否遍历整个图
		memset(visited,0,sizeof(visited));
		check(root);
		for(int i = 1;i <= vertex;i++)
			if(!visited[i])
				return -1;

		mincost = 0;
		while(1)
		{
			//更新最小入边
			for(int i = 1;i <= vertex;i++)
				in[i] = INF;
			for(int i = 0;i < edge;i++)
			{
				int u = adjlist[i].u;
				int v = adjlist[i].v;
				int w = adjlist[i].cost;
				if(in[v] > w && u != v) //u != v
				{
					in[v] = w;
					pre[v] = u;
				}
			}

			//查环
			count = 0;
			in[root] = 0;
			memset(visited,0,sizeof(visited));
			memset(color,0,sizeof(color));
			color[root] = ++count;
			for(int i = 1;i <= vertex;i++)
			{
				mincost += in[i];
				int v = i;
				while(visited[v] != i && !color[v])
				{
					visited[v] = i;
					v = pre[v];
				}
				//对环染色
				if(!color[v])
				{
					//注意,这里v是环上的点,但i不一定是
					color[v] = ++count;
					for(int u = pre[v];u != v;u = pre[u])
						color[u] = count;
				}
			}
			//无环
			if(count == 1)
				return mincost;

			//非环上的点重新设编号
			for(int i = 1;i <= vertex;i++)
				if(!color[i])
					color[i] = ++count;

			//缩点
			for(int i = 0;i < edge;i++)
			{
				int u = adjlist[i].u;
				int v = adjlist[i].v;
				adjlist[i].u = color[u];
				adjlist[i].v = color[v];
				if(color[u] != color[v])
					adjlist[i].cost -= in[v];
			}
			vertex = count;
			root = color[root];
		}
		return -1;
	}

	void check(int x)
	{
		visited[x] = 1;
		for(int i = head[x];i != -1;i = adjlist[i].next)
		{
			int v = adjlist[i].v;
			if(!visited[v])
				check(v);
		}
	}
}g;
这是定根的,如果不定根的话,我们可以加一个虚拟结点,它到每个点加一个边,权值为所有权值和 + 1或者更大,设其为INF。在最后返回判断的时候,min - INF < INF,则图产有生成树有权值和最小为min - INF;否则,没有生成树。不定根的时候,我们可以知道,因为新增的点到所有点都有边,所以可以略去step1。

另外,不定根输出定点的时候,可以在结构体Node里面加两个属性realu,realv,表示它实际的点,而我们计算的时候(比如缩点)用的则u和v。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值