POJ2728 Desert King 最优比率生成树

Problem Address:http://poj.org/problem?id=2728


【前言】


我确实是一拿到手就把它当MST做了。

但是经历了一次TLE后我居然翻出算法导论硬生生地把Prim算法的复杂度变小了。

也才发现原来一直以来我写的Prim都是n^3的,而优化了的Prim是O(E+VlgV)。

嗯,确实是一个不小的收获。

但是发现Prim错了。

于是翻出了解题报告,看到了最优比率生成树。

嗯,又有收获了。


【思路】


先说说Prim。

以前写Prim就是三重循环,第一重循环处理n个点,第二重循环找未访问的点以处理,第三重循环找该点的最小值。

后两重循环实际是用来查找最小值的。

然后重温了一下算法导论。

第一重循环处理n个点,第二重由于有key[ ]和π[ ]维护,所以找最小值的时候是lg(n)【实际上我直接还是按n算】,更新时为n。

所以总复杂度为O(E+VlgV)。

事实上两者的不同是思路上的优化。


接下来说最优比率生成树。

实际上它是Prim+二分或迭代。

可以参考:http://www.cnblogs.com/lotus3x/archive/2009/03/21/1418480.html 和 http://blog.csdn.net/cicirise/article/details/3904362,里面都讲的很全面。

和上一篇文章类似,二分实际上就是找一个比率,然后看看在这个比率的作用下的结果,然后用过不断进行Prim运算,调整比率,直到满足条件。

而迭代则是每次Prim得出一个rate,然后不断进行Prim运算从而更新rate。

在这个题目中二分的次数比rate的次数高出很多,所以迭代的时间比较小。

除此之外,在代码方面,二分和迭代都是差不多的。

还有一点要注意的是,上述第一个链接是以c的总和作为二分的上限,但是在这道题中将其作为上限则会超时。这里定上限为1000则可通过。


【代码】


二分:


#include <iostream>
#include <cmath>
using namespace std;

const int maxn = 1000;
const double MAX = 99999999.00;

double map[maxn+5][maxn+5];
double c[maxn+5][maxn+5];
double b[maxn+5][maxn+5];
int x[maxn+5], y[maxn+5];
int z[maxn+5];
int visited[maxn+5];
double key[maxn+5];
int fa[maxn+5];

double MST(int n, double mid)
{
	int ct = 1;
	double temp;
	int tb, tp;
	int i, j;
	for (i=0; i<n; i++)
		visited[i] = 0;
	for (i=0; i<n; i++)
	{
		map[i][i] = MAX;
		for (j=i+1; j<n; j++)
		{
			map[i][j] = map[j][i] = c[i][j] - mid * b[i][j];
		}
	}
	for (i=1; i<n; i++)
	{
		key[i] = map[0][i];
		fa[i] = 0;
	}
	visited[0] = 1;
	double sum = 0.0;
	while(ct<n)
	{
		temp = MAX;
		for (i=0; i<n; i++)
		{
			if (visited[i]==0 && key[i]<temp)
			{
					temp = key[i];
					tb = fa[i];
					tp = i;
			}
		}
		visited[tp] = 1;
		for (i=0; i<n; i++)
		{
			if (visited[i]==0 && map[tp][i]<key[i])
			{
				key[i] = map[tp][i];
				fa[i] = tp;
			}
		}
		ct++;
		sum += temp;
	}
	return sum;
}

int main()
{
	int n;
	int i, j;
	double high, mid, low;
	double minb, t;
	while(scanf("%d", &n)!=EOF)
	{
		if (n==0) break;
		for (i=0; i<n; i++)
		{
			scanf("%d %d %d", &x[i], &y[i], &z[i]);
		}
		high = 0.0;
		minb = MAX;
		for (i=0; i<n; i++)
		{
			b[i][i] = c[i][i] = MAX;
			for (j=i+1; j<n; j++)
			{
				c[i][j] = c[j][i] = (double)abs(z[i]-z[j]);
				b[i][j] = b[j][i] = sqrt((double)((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
				if (b[i][j]<minb)
					minb = b[i][j];
			//	high += c[i][j];
			}
		}
	//	high /= minb;
		high = 1000.0;
		low = 0.0;
		while(high>low)
		{
			mid = (high+low)/2;
			t = MST(n, mid);
			if (t>1e-4)
				low = mid;
			else if (t<-1e-4)
				high = mid;
			else
				break;
		}
		printf("%.3lf\n", mid);
	}
	return 0;
}


迭代:


#include <iostream>
#include <cmath>
using namespace std;

const int maxn = 1000;
const double MAX = 99999999.00;

double map[maxn+5][maxn+5];
double c[maxn+5][maxn+5];
double b[maxn+5][maxn+5];
int x[maxn+5], y[maxn+5];
int z[maxn+5];
int visited[maxn+5];
double key[maxn+5];
int fa[maxn+5];

double MST(int n, double rate)
{
	int ct = 1;
	double temp;
	int tb, tp;
	int i, j;
	for (i=0; i<n; i++)
		visited[i] = 0;
	for (i=0; i<n; i++)
	{
		map[i][i] = MAX;
		for (j=i+1; j<n; j++)
		{
			map[i][j] = map[j][i] = c[i][j] - rate * b[i][j];
		}
	}
	for (i=1; i<n; i++)
	{
		key[i] = map[0][i];
		fa[i] = 0;
	}
	visited[0] = 1;
	double tola = 0.0, tolb = 0.0;
	while(ct<n)
	{
		temp = MAX;
		for (i=0; i<n; i++)
		{
			if (visited[i]==0 && key[i]<temp)
			{
					temp = key[i];
					tb = fa[i];
					tp = i;
			}
		}
		visited[tp] = 1;
		for (i=0; i<n; i++)
		{
			if (visited[i]==0 && map[tp][i]<key[i])
			{
				key[i] = map[tp][i];
				fa[i] = tp;
			}
		}
		ct++;
		tola += c[tb][tp];
		tolb += b[tb][tp];
	}
	return tola/tolb;
}

int main()
{
	int n;
	int i, j;
	double rate, cur;
	while(scanf("%d", &n)!=EOF)
	{
		if (n==0) break;
		for (i=0; i<n; i++)
		{
			scanf("%d %d %d", &x[i], &y[i], &z[i]);
		}
		for (i=0; i<n; i++)
		{
			b[i][i] = c[i][i] = MAX;
			for (j=i+1; j<n; j++)
			{
				c[i][j] = c[j][i] = (double)abs(z[i]-z[j]);
				b[i][j] = b[j][i] = sqrt((double)((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
			}
		}
		rate = 0.0;
		cur = MST(n, rate);
		while(fabs(rate-cur)>1e-4)
		{
			rate = cur;
			cur = MST(n, rate);
		}
		printf("%.3lf\n", cur);
	}
	return 0;
}


【P.S】


退出了数据挖掘。

放弃了软件杯比赛。

在ACM上好好加油!


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值