poj2728

题意:一个无向图,每条边有两个权值,c和l,要求一个生成树,使得所有边的c的和比上l的和最小。

分析:最优比例生成树。现在要求(c1+...)/(l1+...),设一个比例值变量为r。令r>=(c1+...)/(l1+...)。现在题目转化为求r的最小值。

假设我们对于一个确定的r可以判断该不等式是否可以满足,那么我们可以用二分查找的方法来求r的最小值,因为r猜大了则可满足,猜小了则不可满足。

然而,我们确实可以对于一个确定的r来判定是否可满足不等式,方法如下:

现在r是确定的,相当于已知r。先将不等式整理为如下形式:(r*l1-c1)+...>=0,然后我们使不等式左边尽量大即可。现在不等式左边的的每个括号里都只与一个边有关,把这个括号里的值作为这个边的权值,求图的最大生成树即可使得左边最大。

View Code
#include < iostream >
#include
< cstdio >
#include
< cstdlib >
#include
< cstring >
#include
< cmath >
using namespace std;

#define maxn 1005
#define eps 0.0001
#define inf 1000000000

struct Village
{
int x, y, z;
} village[maxn];

double dist[maxn][maxn], map[maxn][maxn], mindist[maxn];
int height[maxn][maxn], n;
bool vis[maxn];

double getdist( const Village & a, const Village & b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

void input()
{
for ( int i = 0 ; i < n; i ++ )
scanf(
" %d%d%d " , & village[i].x, & village[i].y, & village[i].z);
for ( int i = 0 ; i < n; i ++ )
for ( int j = i + 1 ; j < n; j ++ )
{
dist[i][j]
= dist[j][i] = getdist(village[i], village[j]);
height[i][j]
= height[j][i] = abs(village[i].z - village[j].z);
}
}

void makemap( double mid)
{
for ( int i = 0 ; i < n; i ++ )
for ( int j = i + 1 ; j < n; j ++ )
map[j][i]
= map[i][j] = - mid * dist[i][j] + height[i][j];
}

double prim( double dist[])
{
double ans = 0 ;
for ( int i = 0 ; i < n; i ++ )
dist[i]
= inf;
memset(vis,
0 , sizeof (vis));
dist[
0 ] = 0 ;
while ( 1 )
{
double mindist = inf;
int mini = - 1 ;
for ( int i = 0 ; i < n; i ++ )
if (dist[i] < mindist && ! vis[i])
{
mindist
= dist[i];
mini
= i;
}
if (mini == - 1 )
return ans;
vis[mini]
= true ;
ans
+= mindist;
for ( int i = 0 ; i < n; i ++ )
if ( ! vis[i] && map[mini][i] < dist[i])
dist[i]
= map[mini][i];
}
}

double binarysearch()
{
double l = 0 , r = 1000000000 ;
while (r - l > eps)
{
double mid = (l + r) / 2 ;
makemap(mid);
double temp = prim(mindist);
if (temp >= 0 )
l
= mid;
else
r
= mid;
}
return l;
}

int main()
{
// freopen("D:\\t.txt", "r", stdin);
while (scanf( " %d " , & n) != EOF && n != 0 )
{
input();
printf(
" %.3f\n " , binarysearch());
}
return 0 ;
}

转载于:https://www.cnblogs.com/rainydays/archive/2011/03/23/1993091.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值