http://poj.org/problem?id=2728
题意:
给出n个点的坐标一级每个点对应的高度,每条边两个值,a[i][j] = fabs(h[i] - h[j])高度差表示修i->j这条路需要a[i][j]的费用,b[i][j]表示路的长度。让你修路,使得所有点都能够互通并且满足总的的费用/路的总长度最小。
思路:
最小比率生成树。按这个链接所讲的01分数规划——最有比率生成树
我们二分枚举解,求最小生成树保证使得值最小。然后得到f(l) == 0的解就是我们的结果。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 1007 using namespace std; /* freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); */ //freopen("din.txt","r",stdin); const double eps = 1e-6; struct node { double x,y,z; }p[N]; double dis[N]; bool vt[N]; double a[N][N]; double b[N][N]; int n; int dblcmp(double x) { if (x > eps) return 1; else if (x < -eps) return -1; else return 0; } void init() { int i,j; for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { a[i][j] = b[i][j] = (i != j)*inf; } } } double prim(double m) { int i,j; double res = 0; for (i = 0; i <= n; ++i) { vt[i] = false; dis[i] = a[0][i] - m*b[0][i]; } dis[0] = 0; vt[0] = true; for (int k = 0; k < n - 1; ++k) { double MIN = inf; for (i = 0; i < n; ++i) { if (!vt[i] && dis[i] < MIN) { j = i; MIN = dis[i]; } } res += MIN; vt[j] = true; for (i = 0; i < n; ++i) { double t = a[j][i] - m*b[j][i];//这里有点巨坑,必须是【j,i】如果是【i,j】的话会tle if (!vt[i] && b[j][i] != inf && dis[i] > t) { dis[i] = t; } } } return res; } int main() { //freopen("input.txt","r",stdin); int i,j; while (~scanf("%d",&n)) { if (!n) break; for (i = 0; i < n; ++i) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z); init(); double r = -1; for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { if (i == j) continue; double cb = sqrt((p[i].x - p[j].x)*(p[i].x - p[j].x) + (p[i].y - p[j].y)*(p[i].y - p[j].y)); double ca = iabs(p[i].z - p[j].z); b[i][j] = b[j][i] = cb; a[i][j] = a[j][i] = ca; r = max(r,ca); } } double l = 0; double mid = 0; while (dblcmp(l - r) < 0) { mid = (l + r)/2; if (prim(mid) >= 0) { l = mid; } else r = mid; } printf("%.3lf\n",mid); } return 0; }
Dinkelbach迭代算法解决
每次prim()求出子问题的最优解,然后代入r,直到在某个范围内即可。。。。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 1007 using namespace std; /* freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); */ //freopen("din.txt","r",stdin); const double eps = 1e-6; struct node { double x,y,z; }p[N]; double dis[N]; bool vt[N]; double a[N][N]; double b[N][N]; int pre[N]; int n; int dblcmp(double x) { if (x > eps) return 1; else if (x < -eps) return -1; else return 0; } void init() { int i,j; for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { a[i][j] = b[i][j] = (i != j)*inf; } } } double prim(double m) { int i,j; double tval = 0,tdis = 0; for (i = 0; i <= n; ++i) { vt[i] = false; dis[i] = a[0][i] - m*b[0][i]; pre[i] = 0; } dis[0] = 0; vt[0] = true; pre[0] = -1; for (int k = 0; k < n - 1; ++k) { double MIN = inf; for (i = 0; i < n; ++i) { if (!vt[i] && dis[i] < MIN) { j = i; MIN = dis[i]; } } tval += a[pre[j]][j];//记录总价值 tdis += b[pre[j]][j];//记录总距离 vt[j] = true; for (i = 0; i < n; ++i) { double t = a[j][i] - m*b[j][i]; if (!vt[i] && b[j][i] != inf && dis[i] > t) { pre[i] = j; dis[i] = t; } } } return tval/tdis;//求的解 } int main() { //freopen("input.txt","r",stdin); int i,j; while (~scanf("%d",&n)) { if (!n) break; for (i = 0; i < n; ++i) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z); init(); for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { if (i == j) continue; double cb = sqrt((p[i].x - p[j].x)*(p[i].x - p[j].x) + (p[i].y - p[j].y)*(p[i].y - p[j].y)); double ca = iabs(p[i].z - p[j].z); b[i][j] = b[j][i] = cb; a[i][j] = a[j][i] = ca; } } double s = 0,r = 0; while (1) { r = prim(s); if (dblcmp(s - r) == 0) break; s = r; } printf("%.3lf\n",r); } return 0; }