POJ 2728 Desert King (最优比例生成树)

POJ2728 无向图中对每条边i 有两个权值wi 和vi 求一个生成树使得 (w1+w2+...wn-1)/(v1+v2+...+vn-1)最小。

采用二分答案mid的思想。

将边的权值改为 wi-vi*mid.

对所有边求和后除以v 即为 (w1+w2+...wn-1)/(v1+v2+...+vn-1)-mid. 因此,若当前生成树的权值和为0,就找到了答案。否则更改二分上下界。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;

const int maxn=1000;
int n;
double lenth[maxn],nowans,lef,righ,w[maxn][maxn],v[maxn][maxn];
bool intree[maxn];

double ab(double x)
{
 if(x>0)return x;
 	else return x*(-1);
}

class point
{
 public:
 double x;double y;double z;
};


point po[maxn];

double dist(point a,point b);




double cost(point a,point b)
{
 return ab((a.z-b.z));
}


class edge
{
 public:
 int  pa;int pb;
 double dis;double cos;double div;	
 edge(int a,int b,double(*distance)(point,point),double (*cost)(point,point))
 {
  this->pa=a;this->pb=b;
  this->dis=distance(po[a],po[b]);
  this->cos=cost(po[a],po[b]);
  this->div=cos/dis;
 }
 bool operator <(const edge b)const
 {
  return (this->cos-(this->dis*nowans))>(b.cos-(b.dis*nowans));//这个算法的核心 
 }
};

double dist(point a,point b)
{
 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double min(double a,double b)
{
 if(a<b)return a;
 	else return b;
}




int main()
{
 ios::sync_with_stdio(false);
 while(cin>>n)
 {
   if(n==0)return 0;
   lef=0.0;righ=1e6;
   for(int i=0;i<n;i++)
    	{
 	     cin>>po[i].x>>po[i].y>>po[i].z;
	    }
  for(int i=0;i<n;i++)
  	for(int j=0;j<n;j++)
  		{
  		 w[i][j]=cost(po[i],po[j]);
  		 v[i][j]=dist(po[i],po[j]);
		}
   double minnow;
  while(true)
   	{
   	 nowans=(lef+righ)/2;minnow=0;
   	 memset(intree,0,sizeof(intree));
   	 for(int i=1;i<n;i++)
   	 	{
   	 	 lenth[i]=w[0][i]-v[0][i]*nowans;
		}
   	 lenth[0]=0.0;intree[0]=true;
   	 for(int i=1;i<n;i++)
   	 	{
   	 	 double temp=1e+30;int tj=0;
   	 	 for(int j=1;j<n;j++)
   	 	 	if(!intree[j]&&lenth[j]<temp)
   	 	 		{
   	 	 		 tj=j;
   	 	 		 temp=lenth[j];
				}
		 minnow+=lenth[tj];
		 intree[tj]=true;
		 for(int j=1;j<n;j++)
		 	{
		 	 if(!intree[j])lenth[j]=min(lenth[j],w[tj][j]-v[tj][j]*nowans);
			}
		}
	 if(ab(minnow-0.0)<1e-5)break;
	 if(minnow>0)lef=nowans;
	 	else righ=nowans;
	}
  printf("%.3lf\n",(lef+righ)/2);
 }
 return 0;
}

  用二分答案思想解决的生成树问题还有单度限制最小生成树,参考CODEFORCES 125E.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值