POJ-3164 Command Network (朱刘算法)

这是一道最小树形图的模板题

朱刘算法开始时的确不是太好理解,在网上看了好多文章才差不多理解。

在这里说一点,缩点时,如果弧(u,v)的v点在一个环中,这个环形成的缩点在新图中的编号是k,那么新图中(u,k)的权值是W(u,v)-in[v],因为ret(即最终的返回值)只在朱刘算法的开始置了一次0,这个权值的变化可以保证,在对一个点加入新的入边时,可以顺便把上一次的旧边的权值在ret中去掉,对于不在环上的点,也是这样

代码如下(模板来自kuangbin)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 105
#define maxm 10005
using namespace std;
struct Edge
{
	int u, v;
	double cost;
};
Edge edge[maxm];
int pre[maxn], vis[maxn],id[maxn];
double in[maxn],xy[maxn][2];
inline double cal_dis(int i,int j)
{
	return sqrt(pow(xy[i][0] - xy[j][0], 2) + pow(xy[i][1] - xy[j][1], 2));
}
//root为根的序号,n为点数,m为有效的边的数量(即去除掉自环)
double liuzhu(int root, int n, int m)
{
	int v,tn;
	double ret = 0;
	while (1)
	{
		for (int i = 1; i <= n; i++)
			in[i] = -1;//-1标志该入边不存在
		for(int i=0;i<m;i++)
			if (edge[i].cost < in[edge[i].v]|| in[edge[i].v]<0)
			{
				in[edge[i].v] = edge[i].cost;
				pre[edge[i].v] = edge[i].u;
			}
		for (int i = 1; i <= n; i++)
			if (i != root && in[i]<0)
				return -1;
		tn = 0;
		in[root] = 0;
		memset(id, -1, sizeof(id));
		memset(vis, -1, sizeof(vis));
		for (int i = 1; i <= n; i++)
		{
			ret += in[i];
			v = i;
			while (vis[v] != i && id[v] == -1&&v!=root)
			{
				vis[v] = i;
				v = pre[v];
			}
			if (id[v] == -1 && v != root)
			{
				tn++;//这里点的序号从1开始,所以tn也要先加
				for (int u = pre[v]; u != v; u = pre[u])
					id[u] = tn;
				id[v] = tn;
			}
		}
		if (!tn) break;
		for (int i = 1; i <= n; i++)
			if (id[i] == -1)
				id[i] = ++tn;
		for (int i = 0; i < m;)
		{
			v = edge[i].v;
			edge[i].u = id[edge[i].u];
			edge[i].v = id[edge[i].v];
			if (edge[i].u != edge[i].v)
				edge[i++].cost -= in[v];
			else
				swap(edge[i], edge[--m]);//如果是环中的边,抛弃该边,并把总边数-1
		}
		n = tn;
		root = id[root];
	}
	return ret;
}
int main()
{
	int n, m, lcnt;
	double t;
	while (scanf("%d%d", &n, &m) != EOF)
	{
		for (int i = 1; i <= n; i++) scanf("%lf%lf", &xy[i][0], &xy[i][1]);
		lcnt = 0;//用于统计有效的弧的数量
		for (int i = 0,u,v; i < m; i++)
		{
			scanf("%d%d", &u, &v);
			if (u != v)
			{
				edge[lcnt].u = u;
				edge[lcnt].v = v;
				edge[lcnt++].cost = cal_dis(u, v);
			}
		}
		if (lcnt < n - 1) t = -1;//如果非自弧的数量<n-1,肯定无法形成树形图
		else t = liuzhu(1, n, lcnt);
		if (t < 0) printf("poor snoopy\n");
		else printf("%.2lf\n", t);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值