Desert King(poj 2728)
题目类型:0/1分划之最小比率生成树
解题思路:假设费用之和为A,长度之和为B,题意为求解A/B的最小值。
采用二分的思路,假设某结果为ans,
若存在一组解,使得A-Bans<0,即代表最小值比ans小,因此移动上界。为了找到sigma(ai-bians)<0的一组解,采用贪心的思想,去找较小的一组ai-bians,若该组都无法使得A-Bans小于0,则代表不存在该组解。
那么如何去找到较小的一组ai-bians呢?基于该题要求生成的是一棵以capital village为根的树,因此将ai-bians设置为边值,那么问题也就转化为求解最小生成树。
那么是使用kruskal还是prim呢?n的最大值为1000,即点数n为1000,边数m为5*10^5
,prim算法的复杂度为O(n2),kruskal的时间复杂度为O(m logm),因此选用prim算法。
那么,初始的L和R值应该如何设置呢?设置L为0,寻找ai的最大值,bi的最小值,设置其比值为R的初始值
#include<iostream>
#include<cmath>
using namespace std;
#define NMAX 1003
int N;
double x[NMAX], y[NMAX], z[NMAX];
//double map[NMAX][NMAX];
double length(int i, int j,double R) {
return fabs(z[i] - z[j]) - R * sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
int check(double R) {
//double map[NMAX][NMAX];
double res = 0;
int vis[NMAX];
memset(vis, 0, sizeof(vis));
double dis[NMAX];
for (int i = 0; i < N; i++) dis[i] = length(0,i,R);
vis[0] = 1;
for (int k = 1; k < N ; k++) {
//找最小的边
double MIN = 10000000;
int mlot = 0;
for (int i = 0; i < N; i++) {
if (!vis[i] && dis[i] < MIN) {
MIN = dis[i], mlot = i;
}
}
vis[mlot] = 1;
res += MIN;
//更新
for (int i = 0; i < N; i++) {
if (!vis[i] && length(mlot, i, R) < dis[i] ) dis[i] = length(mlot, i, R);
}
}
//printf("%.3lf\n", res);
if (res < 0) return 1;
else return 0;
}
int main() {
while (1) {
//cin >> N;
scanf_s("%d", &N);
if (!N) break;
//double zmax = 0, ymin = 1e4 + 4;
for (int i = 0; i < N; i++) {
//cin >> x[i] >> y[i] >> z[i];
scanf_s("%lf %lf %lf", &x[i], &y[i], &z[i]);
//if (z[i] > zmax) zmax = x[i];
//if (y[i] < ymin || x[]) ymin = y[i];
}
double L = 0, R = 100.0, M;
while (R - L > 1e-7) {
M = (R + L) / 2;
if (check(M)) R = M;
else L = M;
}
printf("%.3lf\n", R);
}
}
tip: AC不了的原因竟然是没有注释掉printf(“%.3lf\n”, res); 我真的是服了我自己了