1. 程式人生 > 實用技巧 >【POJ2728】沙漠王國Desert King-最優比率生成樹-01分數規劃

【POJ2728】沙漠王國Desert King-最優比率生成樹-01分數規劃

Description

大衛剛被選舉為沙漠王國的國王。為了贏得人們的尊重,他決定修建渠道,把水引到整個國家的每個村莊。使得連線到中心村莊的所有村莊都將會被灌溉。經過多天的研究,他最終制定出目的。就是使建造渠道每英里的平均價格最小。換句話說,建造渠道的總體成本與總長度的比值必須最小。只需要建立必須的渠道,把水引到所有的村莊即可。也就是每個村莊僅一種方式連線到中心村莊。
  已知每個村莊的位置和高度,任意兩個村莊之間的渠道必須是直的。長度為這兩個村莊(x1,y1)和(x2,y2)之間的距離sqrt((x1-x2)2+(y1-y2)2),建立渠道的花費為這兩個村莊高度差的絕對值。每個村莊都在不同高度,注意水能夠通過特殊的裝置從低處流到高處(不考慮裝置費用),沒有任意三個村莊位於同一條水平線上。現在國王想知道建造渠道每英里的平均價格最小是多少?

Input

輸入有多組測試資料;
  每組測試資料第一行包含一個整數N(2<=N<=1000),表示村莊的個數。
  接下來n行,每行三個整數x,y和z(0<=x,y<10000,0<=z<10000000),表示一個村莊位於(x,y)這點,高度為z,檔案最後一行以一個0作為結束,不用處理。

Output

對於每組測試資料,輸出一行為建造渠道的總體成本與總長度的比值,保留三位小數。

Sample Input

4
0 0 0
0 1 1
1 1 2
1 0 3
0

Sample Output

1.000


思路

  • 最優比率生成樹(01分數規劃運用例項)
  • 完全圖用prim
  • 標準上界應該為high之和/min(dis),但1e5也能過

程式碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int n,flag[1005];
double dis[1005][1005],high[1005][1005],d[1005];
struct fdfdfd{double x,y,z;}e[1005];
double js(int i,int j){return sqrt((e[i].x-e[j].x)*(e[i].x-e[j].x)+(e[i].y-e[j].y)*(e[i].y-e[j].y));}
bool prim(double x)
{
	double sum=0;
	memset(flag,0,sizeof(flag));
	memset(d,0x7f,sizeof(d)); d[1]=0;
	for(int i=1;i<=n;++i)
	{
		double minn=0x7fffffff; int k=-1;
		for(int j=1;j<=n;++j)
			if(!flag[j]&&d[j]<minn) minn=d[j],k=j;
		flag[k]=1; sum+=d[k];
		for(int j=1;j<=n;++j)
			if(!flag[j]) d[j]=min(d[j],high[k][j]-x*dis[k][j]);
	}
	return (sum>0);
}
int main()
{
	while(scanf("%d",&n))
	{
		if(!n) break;
		for(int i=1;i<=n;++i) scanf("%lf%lf%lf",&e[i].x,&e[i].y,&e[i].z);
		for(int i=1;i<=n;++i)
			for(int j=i+1;j<=n;++j)
			{
				dis[i][j]=dis[j][i]=js(i,j);
				high[i][j]=high[j][i]=fabs(e[i].z-e[j].z);
			}
		double l=0,r=100000;
		while(r-l>1e-8)
		{
			double mid=(r+l)/2.0;
			if(prim(mid)) l=mid;
			else r=mid;
		}
		printf("%.3f\n",l);
	}
	return 0;
}