题目链接

最优比率生成树

题意:给出点的坐标和高度,任意两点之间存在两个权值,一个是高度差的绝对值ci,一个是两点之间的距离leni,求权值$\sum_{i=1}^n\frac{c_{i}}{len_{i}}$最小的生成树。

0/1分数规划问题:在可取可不取的情况下,求最大或者最小\(\sum_{i=1}^n\frac{a_{i}}{b_{i}}\)
二分枚举L
求最大:\(\sum_{i=1}^n(a_{i}-L\times b_{i})\)的最大值小于0,则L不是可行解,否则L是可行解。
求最小:\(\sum_{i=1}^n(a_{i}-L\times b_{i})\)的最小值大于等于0,则L不是可行解,否则L是可行解。
思路:把图的权值改成c - ans\(\times\)len,求最小生成树,二分求最小可行解ans。

注意:稠密图kruskal会超时

#pragma GCC optimize(2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>

using namespace std;

typedef long long ll;

const int MaxnN = 1000+10;
const int MaxnM = 1e6+10;
const int INF = 0x3f3f3f3f;
const ll LINF = 1e18;
const double eps = 1e-6;

int n;
double mincost[MaxnN], c[MaxnN][MaxnN], len[MaxnN][MaxnN];
pair<int, pair<int, int> > p[MaxnN];
bool vis[MaxnN];

double dis(pair<int, int> x, pair<int, int> y) {
	return sqrt((double)(x.first-y.first)*(x.first-y.first)
				+(double)(x.second-y.second)*(x.second-y.second));
}

bool ok(double mid) {
	double res = 0;
	for(int i = 1; i <= n; ++i) {
		mincost[i] = LINF;
		vis[i] = false;
	}
	mincost[1] = 0;
	while(1) {
		int u = -1;
		for(int i = 1; i <= n; ++i) if(!vis[i] && (u == -1 || mincost[u] > mincost[i])) u = i;
		if(u == -1) break;
		vis[u] = true;
		res += mincost[u];
		
		for(int v = 1; v <= n; ++v) {
			if(!vis[v] && mincost[v] > c[u][v]-mid*len[u][v]) {
				mincost[v] = c[u][v]-mid*len[u][v];
			}
		}
	}
	if(res >= eps) return false;
	else return true;
}

int main(void)
{
	while(scanf("%d", &n) != EOF) {
		if(!n) break;
		for(int i = 1; i <= n; ++i) {
			scanf("%d%d%d", &p[i].first, &p[i].second.first, &p[i].second.second);
		}
		double t1, t2;
		for(int i = 1; i <= n; ++i) {
			for(int j = i+1; j <= n; ++j) {
				t1 = abs(p[i].second.second-p[j].second.second);
				t2 = dis(make_pair(p[i].first, p[i].second.first), make_pair(p[j].first, p[j].second.first));
				c[i][j] = c[j][i] = t1;
				len[i][j] = len[j][i] = t2;
			}
		}
		double L = 0, R = 1000.0, mid;
		while(L < R-eps) {
			mid = (L+R)/2;
			if(ok(mid)) R = mid;
			else L = mid+eps;
		}
		printf("%.3f\n", R);
	}
	return 0;
 }