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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章