01分数规划学习笔记
# 01分数规划
求解的最小值,并且有某些奇怪的限制。
我们把的提取出来
我们可以二分一个答案,如果答案的最小值小于等于,那么有
可以化简为,只需判断是否有符合条件的序列即可
# 例题 POJ 2728
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1020;
const double eps = 1e-5; // 开太大会WA,太小(如1e-6)会TLE
double dist[N][N], cur_dist[N], x[N], y[N], w[N];
bool vis[N];
int n;
inline double calc_dist(int i, int j){
return sqrt((x[i]-x[j]) * (x[i]-x[j]) + (y[i]-y[j]) * (y[i]-y[j]));
}
double prim(double ans){
memset(vis, 0, sizeof vis);
vis[0] = 1;
double sum = 0;
for(int i=1; i<n; i++) cur_dist[i] = fabs(w[0]-w[i]) - ans*dist[0][i];
for(int i=1; i<n; i++){
int most = -1;
double extreme;
for(int j=1; j<n; j++){
if(vis[j]) continue;
if(most == -1 || extreme > cur_dist[j]){
most = j;
extreme = cur_dist[j];
}
}
vis[most] = 1;
sum += cur_dist[most];
for(int j=1; j<n; j++){
if(vis[j]) continue;
cur_dist[j] = min(cur_dist[j], fabs(w[j]-w[most]) - ans*dist[j][most]);
}
}
return sum;
}
void solve(){
for(int i=0; i<n; i++) scanf("%lf%lf%lf", &x[i], &y[i], &w[i]);
for(int i=0; i<n; i++){
for(int j=i; j<n; j++)
dist[i][j] = dist[j][i] = calc_dist(i, j);
}
double l=0, r=100, mid; // 理论上r=100是不对的,但是这题开太大会TLE
while(r-l > eps){
mid = (l + r) / 2;
if(prim(mid) <= 0){
r = mid;
}else{
l = mid;
}
}
printf("%.3f\n", l); // 很邪门,用%.3lf会WA
}
signed main(){
while(scanf("%d", &n) == 1 && n) solve();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
上次更新: 2021/02/24, 03:37:30