题目链接:http://poj.org/problem?id=2728
题目大意:
给你一个无向图,每条边有一个价值和长度,求一棵生成树,其对应的价值和与长度和的币值最小。
解题思路:
01分数规划。对每一个 (mid),以 (a_i - mid imes b_i) 为边求最小生成树,判断最小生成树的长度是否 (ge 0)。
示例代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 1010;
int n, x[maxn], y[maxn], z[maxn];
double dis[maxn][maxn], cost[maxn];
bool vis[maxn];
bool prim(double mid) {
memset(vis, 0, sizeof(bool)*n);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < i; j ++) {
dis[i][j] = dis[j][i] = abs(z[i] - z[j]) - mid * sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
}
}
cost[0] = 0;
for (int i = 1; i < n; i ++) cost[i] = 1e9;
double ans = 0;
for (int j = 0; j < n; j ++) {
int u = -1;
for (int i = 0; i < n; i ++) if (!vis[i] && (u == -1 || cost[u] > cost[i])) u = i;
if (u == -1) break;
ans += cost[u];
vis[u] = true;
for (int i = 0; i < n; i ++) if (!vis[i] && dis[u][i] < cost[i]) cost[i] = dis[u][i];
}
return ans >= 0;
}
int main() {
while (~scanf("%d", &n) && n) {
for (int i = 0; i < n; i ++) scanf("%d%d%d", x+i, y+i, z+i);
double L = 0, R = 100.0;
while (R - L > 1e-4) {
double mid = (L + R) / 2;
if (prim(mid)) L = mid;
else R = mid;
}
printf("%.3f
", L);
}
return 0;
}