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

来源:互联网 发布:turn a高达知乎 编辑:程序博客网 时间:2024/05/11 20:07
题目类型  最优比率生成树

题目意思
给出最多 1000 个点与这些点之间的边的长度与花费 找一棵生成树 使  Sum(边的花费) / Sum(边的长度) 的值最小 输出这个值

解题方法
01分数规划 -> 对于0-1分数规划的Dinkelbach算法的分析

这道题求的是最小值
设边的花费为行向量 [c1, c2, ... , cn] 边的长度为行向量 [d1, d2, ... , dn] 选边的情况为列向量 [x1 ; x2 ; .. ; xn]
则求 r = (c*x) / (d*x) 的最小值
设 f(L) = c*x - L*(d*x) 即 f(L) = (c - L*d) x, r最小即L最小
记 z(L) 为 f(L)的最小值 令 x*为分数规划的最优解 令L* = (cx*) / (dx*) (即最小的r)
z(L) > 0 当且仅当 L < L*
z(L) = 0 当且仅当 L = L*
z(L) < 0 当且仅当 L > L*
因此可以用二分法求解, 二分L 对于每个mid求一次z(mid) 如果z(mid)>0则 l = mid 如果 z(mid) < 0 则r = mid

这道题 c 即边的花费 d即边的长度 
二分 L (最小值为0, 最大值为Sum(c)) 时找 f(L) = (c - L*d)x的最小值 如果把 c-L*d 当作边的权值 那么就是求这些边权下的最小生成树

由于这道题是完全图 而kruscal算法的时间复杂度是O(eloge) 会导致超时 因此可以使用时间复杂度为O(n*n)的prime算法求最小生成树

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <vector>#include <algorithm>using namespace std;const int maxn = 1000 + 10;const int INF = 1<<29;struct Node {int x, y, z;}node[maxn];double d[maxn][maxn], w[maxn][maxn], cd[maxn][maxn];int Abs(int x) { return x < 0 ? -x : x; }double dis(int a, int b) {double x = node[a].x - node[b].x;double y = node[a].y - node[b].y;return sqrt( x*x + y*y );}double cal(double x, int n) {for( int i=0; i<n; i++ ) for( int j=0; j<n; j++ ) cd[i][j] = w[i][j] - x * d[i][j];double sum = 0;double dis[maxn];bool vis[maxn];memset(vis, 0, sizeof(vis));vis[0] = true;for( int i=1; i<n; i++ ) dis[i] = cd[0][i];dis[0] = 0;for( int i=1; i<n; i++ ) {double nmin = INF;int mark = 0;for( int j=1; j<n; j++ ) {if(vis[j] == true) continue;if(nmin > dis[j]) {nmin = dis[j];mark = j;}}sum += nmin;vis[mark] = true;for( int j=1; j<n; j++ ) {if(vis[j] == true) continue;if(dis[j] > cd[mark][j]) dis[j] = cd[mark][j];}}return sum;}int main() {freopen("in", "r", stdin);int n;while(scanf("%d", &n), n) {for( int i=0; i<n; i++ ) {scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].z);}double sum = 0;for( int i=0; i<n; i++ ) {for( int j=i+1; j<n; j++ ) {d[j][i] = d[i][j] = dis(i, j);w[j][i] = w[i][j] = Abs(node[i].z-node[j].z);sum += d[i][j];}}double l = 0, r = 1e10;//printf("l = %lf r = %lf\n", l, r);while(r-l>1e-4) {double mid = (l+r)/2;//printf("cla = %lf mid = %lf\n", cal(mid, n), mid);if(cal(mid, n) < 0) r = mid;else l = mid;//return 0;}printf("%.3f\n", l);}return 0;}


0 0
原创粉丝点击