朱刘算法

求有向图的最小生成树

树形图:

(1)没有环。

(2)除了根之外,每个点的入度为1。

最小树形图问题:求一个树形图,使得总边权最小。

步骤:

(1)对于除了根节点以外的每个点,找出所有入边中,权值最小的边。

(2)选出的边是否存在环。如果不存在累加所有边的权值,直接结束。

(3)如果有环的话,将所有点缩点,构造一个新的图。如果有环的话,不会存在两个相交的环,因为环与环之间是没有公共点的。对于边:

1.环内的边直接删去

2.u->v,v在环内,w[u,v] = w[u,v] - w[v在环内的入边]

3.其它边,保持不变

每一次起码会缩一次点,最多迭代n次,算法一定会终止。

证明:
对于出现环的情况:

1.至少去掉一条边。

2.一定存在一个最优解,只去掉环中的一条边。新加进去边时,要把原来这个点进入的边删除。

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
const double INF = 1000000000.0;
const int N = 1005;
int n, m, pre[N];
typedef double db;
db d[N][N], bd[N][N];
pair<db, db> p[N];
bool g[N][N], bg[N][N], vis[N];
db dis(int a, int b)
{
	db x = p[a].x - p[b].x;
	db y = p[a].y - p[b].y;
	return sqrt(x * x + y * y);
}
int tot = 0, scc_cnt = 0, low[N], dfn[N], scc[N];
stack<int> s; 
void tarjan(int x)
{
	dfn[x] = low[x] = ++ tot, s.push(x), vis[x] = 1;
	int i = pre[x];
	if(!dfn[i]) 
	{
		tarjan(i);
		low[x] = min(low[x], low[i]);
	}
	else if(vis[i]) low[x] = min(low[x], low[i]);	
	if(low[x] == dfn[x])
	{
		int y;
		scc_cnt ++;
		do
		{
			y = s.top(); s.pop();
			vis[y] = 0;
			scc[y] = scc_cnt;
 		}while(y != x);
	}
	return ;
}
void dfs(int x)
{
	vis[x] = 1;
	for (int i = 1; i <= n; ++ i)
	{
		if(g[x][i] && !vis[i]) dfs(i); 
	}
	return ;
}
bool check()
{
	dfs(1);
	bool pd = true;
	for (int i = 1; i <= n; ++ i) if(!vis[i]) {pd = false; break; }
	for (int i = 0; i <= n; ++ i) vis[i] = 0;
	return pd;
}
db ZL()
{
	db res = 0;
	for (int i = 1; i <= n; ++ i)
	{
		for (int j = 1 ; j <= n; ++ j) 
		{
			if(g[i][j]) d[i][j] = dis(i, j);
			else d[i][j] = INF;
		}
	}
	while(true)
	{
		for (int i = 1; i <= n; ++ i)
		{
			pre[i] = i;
			for (int j = 1; j <= n; ++ j) 
			{
				if(d[pre[i]][i] > d[j][i]) pre[i] = j;
			}
		}
		for (int i = 1; i <= n; ++ i) dfn[i] = low[i] = vis[i] = scc[i] = 0;
		tot = scc_cnt = 0;
		for (int i = 1; i <= n; ++ i) if(!dfn[i]) tarjan(i);
//		for (int i = 1; i <= n; ++ i) printf("%d ", scc[i]);
//		printf("\n\n");
		if(scc_cnt == n)
		{
			for (int i = 2; i <= n; ++ i) res += d[pre[i]][i];
			break;
		}
		for (int i = 2; i <= n; ++ i) 
		{
			if(scc[pre[i]] == scc[i]) res += d[pre[i]][i];
		}
		for (int i = 1; i <= scc_cnt; ++ i)
		{
			for (int j = 1; j <= scc_cnt; ++ j) bd[i][j] = INF;
 		}
 		for (int i = 1; i <= n; ++ i)
 		{
 			for (int j = 1; j <= n; ++ j)
			{
			 	if(d[i][j] < INF && scc[i] != scc[j])
			 	{
			 		int a = scc[i], b = scc[j];
			 		if(scc[pre[j]] == scc[j]) bd[a][b] = min(bd[a][b], d[i][j] - d[pre[j]][j]);
					else  bd[a][b] = min(bd[a][b], d[i][j]);
			 	}
			}	
		}
		n = scc_cnt;
		for (int i = 1; i <= scc_cnt; ++ i)
		{
			for (int j = 1; j <= scc_cnt; ++ j) d[i][j] = bd[i][j];
		}
	}
	return res;
}
int main()
{
	while(scanf("%d", &n) != EOF)
	{
		scanf("%d", &m);
		for (int i = 1; i <= n; ++ i) scanf("%lf %lf", &p[i].x, &p[i].y);
		for (int i = 1; i <= n; ++ i)
		{
			for (int j = 1; j <= n; ++ j) g[i][j] = 0;//清空aaa 
		}
		for (int i = 1; i <= m; ++ i)
		{
			int a, b; scanf("%d %d", &a, &b);
			if(b != 1 && a != b) g[a][b] = true;
		}
		if(!check()) printf("poor snoopy\n");
		else printf("%.2lf\n", ZL());
	}
	return 0;
}
posted @ 2025-02-13 10:33  Helioca  阅读(56)  评论(0)    收藏  举报
Document