http://poj.org/problem?id=2728
大緻題意:有n個村莊,輸入每個村莊的位置和高度,這n個村莊要連在一起,村與村之間的長度為他們之間的歐幾裡得距離,花費是兩村之間的高度差,要求連在一起的花費和與距離和之比的最小值。
思路:明顯的最優比率生成樹。二分答案λ,每條邊重新賦權c[i] - λd[i] ,因為要求比值最小,那麼對于所有的生成樹,它們的f[λ]必須>=0,是以隻需求得基于最小生成樹的f'[λ],當f'[λ] = 0時即找到了正解λ*。
二分:
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-7
using namespace std;
const _LL INF = 1e18;
const int maxn = 1010;
struct node
{
int x,y,z;
}p[maxn];
int n;
double Map[maxn][maxn];
double cal(int i, int j)
{
return sqrt( 1.0*(p[i].x - p[j].x)*(p[i].x - p[j].x) + 1.0*(p[i].y - p[j].y)*(p[i].y - p[j].y) );
}
double prim(double mid)
{
double dis[maxn];
int vis[maxn];
double sum = 0;
memset(vis,0,sizeof(vis));
for(int i = 1; i <= n; i++)
dis[i] = abs(p[i].z - p[1].z) - Map[1][i] * mid;
vis[1] = 1;
for(int i = 1; i <= n-1; i++)
{
double Min = INF;
int pos = -1;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dis[j] < Min)
{
Min = dis[j];
pos = j;
}
}
if(pos == -1)
break;
sum += Min;
vis[pos] = 1;
double tmp;
for(int j = 1; j <= n; j++)
{
tmp = abs(p[pos].z - p[j].z) - Map[pos][j] * mid;
if(!vis[j] && dis[j] > tmp)
dis[j] = tmp;
}
}
return sum;
}
int main()
{
while(~scanf("%d",&n) && n)
{
for(int i = 1; i <= n; i++)
scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
Map[i][j] = cal(i,j);
}
double l = 0.0, r = 40.0;
double mid;
while(fabs(r-l) > eps)
{
mid = (l+r)/2;
if( prim(mid) >= 0)
l = mid;
else r = mid;
}
printf("%.3f\n",mid);
}
return 0;
}
疊代
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-7
using namespace std;
const _LL INF = 1e18;
const int maxn = 1010;
struct node
{
int x,y,z;
}p[maxn];
int n;
double Map[maxn][maxn];
double cal(int i, int j)
{
return sqrt( 1.0*(p[i].x - p[j].x)*(p[i].x - p[j].x) + 1.0*(p[i].y - p[j].y)*(p[i].y - p[j].y) );
}
double prim(double mid)
{
double cost,len,sum;
double dis[maxn];
int vis[maxn];
int pre[maxn];
sum = 0;
cost = 0;
len = 0;
memset(vis,0,sizeof(vis));
memset(pre,-1,sizeof(pre));
for(int i = 1; i <= n; i++)
{
dis[i] = abs(p[i].z - p[1].z) - Map[1][i] * mid;
pre[i] = 1;
}
vis[1] = 1;
for(int i = 1; i <= n-1; i++)
{
double Min = INF;
int pos = -1;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dis[j] < Min)
{
Min = dis[j];
pos = j;
}
}
if(pos == -1)
break;
sum += Min;
cost += abs(p[ pre[pos] ].z - p[pos].z);
len += Map[pre[pos]][pos];
vis[pos] = 1;
double tmp;
for(int j = 1; j <= n; j++)
{
tmp = abs(p[pos].z - p[j].z) - Map[pos][j] * mid;
if(!vis[j] && dis[j] > tmp)
{
dis[j] = tmp;
pre[j] = pos;
}
}
}
return cost/len;
}
int main()
{
while(~scanf("%d",&n) && n)
{
for(int i = 1; i <= n; i++)
scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
Map[i][j] = cal(i,j);
}
double l = 0.0,r;
while(1)
{
r = prim(l);
if(fabs(r-l) < eps) break;
l = r;
}
printf("%.3f\n",r);
}
return 0;
}