題目鏈接:http://poj.org/problem?id=2728
題目大意:
給你一個無向圖,每條邊有一個價值和長度,求一棵生成樹,其對應的價值和與長度和的幣值最小。
解題思路:
01分數規劃。對每一個 \(mid\),以 \(a_i - mid \times 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\n", L);
}
return 0;
}