poj 3164 <朱刘算法《模板》求最小树形图>

题目链接:  poj 3164


有向图的最小树形图:此算法由朱永津和刘振宏在1965年发表。

膜拜--

写下自己理解的思路---    一天一道题----痛并快乐着--

解题步骤:

1》先从根节点DFS一下,看是否有解--

2》寻找除根节点外的所有点的最小入边--

3》从所有点向上寻找--看中间路径是否存在环--(如果不存在环,说明已经找到最小树形图--将各边加入ans中--算法结束)

4》如果存在环的话就先将环上各边之和加入ans--然后找各点的其他入边并更新(因为已将最小入边加上--其他入边以后如果还用时就应该用它与最小入边的差值)

---然后-收缩环为环中一点--除收缩点外,其他点与外界的状态全部转移到收缩点--其他点标记一下(隐藏)--然后返回步骤2.


图文可在百度文库下载:最小树形图



代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm> 
using namespace std;
int n,m;
#define M 999999999
double x[120],y[120];
double map[120][120];
bool fafe[120];
double dist(int xx,int yy)
{
	double lp=sqrt((x[xx]-x[yy])*(x[xx]-x[yy])+(y[xx]-y[yy])*(y[xx]-y[yy]));
	return lp;
}
int fer[120];
double zhuliu()
{
	int pre[120],i;
	bool visit[120]; 
	double ans=0;
	for (int i=1;i<=n;i++)
	{
		fafe[i]=false;
		map[i][i]=M;
	}
	pre[1]=1;
	while (true)
	{
		for (i=2;i<=n;i++)
		{
			if (fafe[i])continue;
			pre[i]=i;
			for (int j=1;j<=n;j++)
			{
				if (!fafe[j]&&map[j][i]<map[pre[i]][i])
				pre[i]=j;
			}
		}
		for (i=2;i<=n;i++)
		{
			if (fafe[i])continue;
			memset(visit,false,sizeof(visit));
			visit[1]=true;
			int j=i;
			do
			{
				visit[j]=true;
				j=pre[j];
			}while(!visit[j]);
			if (j==1) continue;//从根节点出发--到i之间无环。
			i=j;/*这里是i=j,,,写成j=i就超时--- 如果有环时j不是回到i了吗?? 
			原来是从i想上找---循环可能没有i---如:3——4---5----4:pre[3]=4--pre[4]=5---pre[5]=4;
			这样的话--如果下面的循环还是从i开始--但是判断结果是就j!=i--永远会不到3的位置--就会 Time Limit Exceeded*/
			do
			{
				ans+=map[pre[j]][j];
				j=pre[j];
			}while (j!=i);
			j=i;
			do//改变权值 
			{
				for (int k=1;k<=n;k++)
				{
					if (fafe[k]) continue;
					if (map[k][j]<M&&k!=pre[j])
					map[k][j]-=map[pre[j]][j];
				}
				j=pre[j];
			}while (j!=i);
			for (j=1;j<=n;j++)//将与环上的点相连的路全部连在i上 
			{
				if (j==i) continue;
				for (int k=pre[i];k!=i;k=pre[k])
				{
					if (map[k][j]<map[i][j]) map[i][j]=map[k][j];
					if (map[j][k]<map[j][i]) map[j][i]=map[j][k];
				}
			}
			for (j=pre[i];j!=i;j=pre[j])//除i点,其他点隐藏 
			fafe[j]=true;
			break;// 此环已压缩--当做一点--重新找每点的最短入边-- 
		}
		if (i==n+1)//从2到n都无环--- 
		{
			for (int j=2;j<=n;j++)
			if (!fafe[j])
			ans+=map[pre[j]][j];
			break;
		}
	}
	return ans;
}
void DFS(int xx)
{
	fafe[xx]=false;
	for (int i=1;i<=n;i++)
	if (fafe[i]&&map[xx][i]!=M)
	DFS(i);
	return ;
}
int main()
{
	while (~scanf("%d%d",&n,&m))
	{
		for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		map[i][j]=M;
		for (int i=1;i<=n;i++)
		scanf("%lf%lf",&x[i],&y[i]);
		int a,b;
		for (int i=0;i<m;i++)
		{
			scanf("%d%d",&a,&b);
			if (a==b) continue;
			map[a][b]=dist(a,b);
		}
		bool falg=true;
		memset(fafe,true,sizeof(fafe));
		DFS(1);
		for (int i=1;i<=n;i++)
		if (fafe[i])
		{
			falg=false;
			break;
		}
		if (falg)
		printf("%.2lf\n",zhuliu());
		else
		printf("poor snoopy\n");
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值