POJ 2728 Desert King (最小比率生成树,二分/迭代)

 

题意:沙漠里的王国需要修建水渠,连接国都与村庄····。说白了求一棵树,每个点有三个坐标(x,y,z)。边的benifit为两点之间的距离,cost为两点的高度差。现在要求一棵树使得 cost / benift 最小。

题解:很显然任意两点之间都有边,所以是一个很稠密的图。用Prime。二分的话2800ms+, 迭代300ms+。

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

#define MAX 1009
#define INF 99999999999
#define eps 1.0e-6  // 精度除以(0.001/2 )

struct NODE
{
	int x, y, z;
} node[MAX];

double e[MAX][MAX];
double dis[MAX];
bool vis[MAX];

void build_map ( int n, double l )
{
	for ( int i = 1; i <= n; i++ )
		for ( int j = i + 1; j <= n; j++ )
		{
			double len = sqrt(0.0 + (node[i].x-node[j].x) 

* (node[i].x-node[j].x) + (node[i].y-node[j].y) * (node[i].y-node

[j].y));
	        double cost = fabs(0.0+node[i].z - node[j].z);
	        e[i][j] = e[j][i] = cost - l * len;
		}
}

double prime ( int n )
{
	int i, j, k;
	double minc, ans = 0.0;
	for ( i = 1; i <= n; i++ )
	{
		dis[i] = e[1][i];
		vis[i] = false;
	}
	dis[1] = 0; vis[1] = true;

	for ( i = 2; i <= n; i++ )
	{
		minc = INF; k = -1;
		for ( j = 1; j <= n; j++ )
		{
			if ( !vis[j] && minc > dis[j] )
			{
				minc = dis[j];
				k = j;
			}
		}
		if ( minc == INF ) break;
		ans += minc;
		vis[k] = true;
		for ( j = 1; j <= n; j++ )
			if ( ! vis[j] && dis[j] > e[k][j] )
				dis[j] = e[k][j];
	}
	return ans;
}


int main()
{
	int n;
	while ( scanf("%d",&n) && n )
	{
		for ( int i = 1; i <= n; i++ )
			scanf("%d%d%d",&node[i].x, &node[i].y, &node

[i].z);

	    double mid, temp;
		double high = 100, low = 0.0;
		while ( 1 )
		{
			mid = ( high + low ) / 2;
			build_map ( n, mid ) ;
			temp = prime ( n );
			if ( fabs(temp) <= eps ) break;
			if ( temp < 0.0 )
				high = mid - eps;
			else
				low = mid + eps;
		}
		printf("%.3lf\n",mid);
	}
	return 0;
}


 

迭代法:

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

#define MAX 1001
#define INF 99999999999
#define eps 1.0e-6

struct NODE
{
	int x, y, z;
} node[MAX];

double edge[MAX][MAX];
double len[MAX][MAX];
double cost[MAX][MAX];
double dis[MAX];
bool vis[MAX];
int pre[MAX];

void build_map ( int n, double l )
{
	for ( int i = 1; i <= n; i++ )
		for ( int j = i + 1; j <= n; j++ )
		{
			len[i][j] = len[j][i] = sqrt(0.0 + (node[i].x-node[j].x) * (node[i].x-node[j].x) + (node[i].y-node[j].y) * (node[i].y-node[j].y));
	        cost[i][j] = cost[j][i] = fabs ( 0.0 + node[i].z - node[j].z);
	        edge[i][j] = edge[j][i] = cost[i][j] - l * len[i][j];
		}
}

double prime ( int n )  // 注意,与普通prime有区别
{
	int i, j, k;
	double minc, b = 0, c = 0;
	for ( i = 1; i <= n; i++ )
	{
		dis[i] = edge[1][i];
		pre[i] = 1;     // 所有点的前驱初始化为1
		vis[i] = false;
	}

	dis[1] = 0;
	vis[1] = true;
	for ( i = 2; i <= n; i++ )
	{
		minc = INF; k = -1;
		for ( j = 1; j <= n; j++ )
		{
			if ( ! vis[j] && minc > dis[j] )
			{
				minc = dis[j];
				k = j;
			}
		}
		if ( minc == INF ) break;
		b += len[pre[k]][k];     //*********
		c += cost[pre[k]][k];    //*********
		vis[k] = true;
		for ( j = 1; j <= n; j++ )
			if ( ! vis[j] && dis[j] > edge[k][j] )
			{
				dis[j] = edge[k][j];
				pre[j] = k;   //修改前驱
			}
	}
	return c / b;
}


int main()
{
	int n;
	while ( scanf("%d",&n) && n )
	{
		for ( int i = 1; i <= n; i++ )
			scanf("%d%d%d",&node[i].x, &node[i].y, &node[i].z);

		double a = 0, b;
		while ( 1 )
		{
			build_map ( n, a );
			b = prime ( n );
			if ( fabs(b-a) <= eps ) break;
			a = b;
		}
		printf("%.3lf\n",b);
	}
	return 0;
}

			


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值