POJ-2728-Desert King

POJ-2728-Desert King

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

最优比率生成树

有带权图G, 对于图中每条边e[i], 都有benifit[i](收入)和cost[i](花费), 我们要求的是一棵生成树T, 它使得 ∑(benifit[i]) / ∑(cost[i]), i∈T 最大(或最小).

这显然是一个具有现实意义的问题

解法

设x[i]等于1或0, 表示边e[i]是否属于生成树.

则我们所求的比率 r = ∑(benifit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤i<m .

为了使 r 最大, 设计一个子问题, 让 z = ∑(benifit[i] * x[i]) - l * ∑(cost[i] * x[i]) = ∑(d[i] *x[i]) 最大 (d[i] = benifit[i] - l * cost[i]) , 并记为z(l). 我们可以地把z(l)看做以d为边权的最大生成树的总权值.

显然最优解l’使z(l)=0

因为z(l)单调递减,所以

z(l)>0时,l<l’

z(l)=0时,l=l’

z(l)<0时,l>l’

然后二分搜索就解决了

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define MAX 1009
#define INF 99999999999
#define eps 1.0e-6  
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<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#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、付费专栏及课程。

余额充值