[POJ 3164]Command Network(最小树形图,朱刘算法)

文章目录

title

solution

读完翻译后,很明显就是个朱刘算法的板子题
在这里插入图片描述

最小树形图,就是给出一个带权有向图
从中指定一个特殊的结点 root
求一棵以 root 为根的有向生成树 T,且使得 T 中所有边权值最小
简单来说,最小树形图就是有向图的最小生成树

朱刘算法分为四个过程:
1)求最短弧集合 E

集合E:对于每个点 v i v_i vi(根除外), v i v_i vi的入边最小权值 i n [ v i ] in[v_i] in[vi]所构成的集合

2)判断集合 E 中有没有有向环,如果有转步骤 3,否则转步骤 4

3)收缩点,把有向环收缩成一个点,并且对图重新构建,包括边权值的改变和点的处理,之后再转步骤 1

权值改变方式:
1.该边两个点属于同一个环——权值保持不变
2.该边两个点属于不同环——权值➖边的入点(即有向边指向的那个点 v v v)的最小入边权值 i n [ v ] in[v] in[v]

4)展开收缩点,求得最小树形图
在这里插入图片描述

code

#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define MAXN 105
#define inf 0x3f3f3f3f
struct node {
	int u, v;
	double w;
	node(){}
	node( int U, int V, double W ) {
		u = U, v = V, w = W;
	}
}edge[MAXN * MAXN];
double x[MAXN], y[MAXN], in[MAXN];
int vis[MAXN], id[MAXN], pre[MAXN];

double pigcow( int rt, int n, int m ) {
	memset( id, 0, sizeof( id ) );
	double ans = 0;
	while( 1 ) {
		for( int i = 1;i <= n;i ++ ) in[i] = inf;
		for( int i = 1;i <= m;i ++ ) {
			int u = edge[i].u, v = edge[i].v;
			double w = edge[i].w;
			if( w < in[v] && u != v )
				pre[v] = u, in[v] = w;
		} 
		for( int i = 1;i <= n;i ++ ) {
			if( i == rt ) continue;
			if( in[i] == inf ) return -1;
		}
		int cnt = 0;
		in[rt] = 0;
		memset( id, 0, sizeof( id ) );
		memset( vis, 0, sizeof( vis ) ); 
		for( int i = 1;i <= n;i ++ ) {
			ans += in[i];
			int t = i;
			while( vis[t] != i && ! id[t] && t != rt )
				vis[t] = i, t = pre[t];
			if( t != rt && ! id[t] ) {
				++ cnt;
				for( int fa = pre[t];fa != t;fa = pre[fa] )
					id[fa] = cnt;
				id[t] = cnt;
			}
		}
		if( ! cnt ) break;
		for( int i = 1;i <= n;i ++ )
			if( ! id[i] ) id[i] = ++ cnt;
		for( int i = 1;i <= m;i ++ ) {
			int u = edge[i].u, v = edge[i].v;
			edge[i].u = id[u], edge[i].v = id[v];
			if( id[u] != id[v] ) edge[i].w -= in[v];
		}
		n = cnt;
		rt = id[rt];
	}
	return ans; 
}

double calc( int u, int v ) {
	return sqrt( ( x[u] - x[v] ) * ( x[u] - x[v] ) + ( y[u] - y[v] ) * ( y[u] - y[v] ) );
}

int main() {
	int n, m;
	while( ~ scanf( "%d %d", &n, &m ) ) {
		for( int i = 1;i <= n;i ++ )
			scanf( "%lf %lf", &x[i], &y[i] ); 
		for( int i = 1, u, v;i <= m;i ++ ) {
			scanf( "%d %d", &u, &v );
			edge[i] = node( u, v, calc( u, v ) );
		}
		double ans = pigcow( 1, n, m );
		if( ans != -1 ) printf( "%.2f\n", ans );
		else printf( "poor snoopy\n" );
	}
	return 0;
} 

其实就是为了存个板子,哈哈哈哈哈
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值