0/1分数规划
从该题可以归纳出的0/1分数规划的一般模型:给定正整数
a
1
,
a
2
.
.
.
a
n
a_{1},a_{2}...a_{n}
a1,a2...an以及
b
1
,
b
2
.
.
.
b
n
b_{1}, b_{2}...b_{n}
b1,b2...bn从中选出若干对的a和b的和的商,求商的最大值Max或者最小值Min,即:
∑
a
[
i
]
∑
b
[
i
]
∈
[
M
i
n
,
M
a
x
]
\frac{\sum_{}a[i]}{\sum_{}b[i]}∈[Min, \ Max]
∑b[i]∑a[i]∈[Min, Max]
由于不知道他们的商是多少,我们不妨先假设 ∑ a [ i ] ∑ b [ i ] = m \frac{\sum_{}a[i]}{\sum_{}b[i]}=m ∑b[i]∑a[i]=m
find_Min
1.若 任意
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
≥
0
\sum (a[i] - m * b[i]) ≥ 0
∑(a[i]−m∗b[i])≥0 ⇔
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
m
i
n
≥
0
\sum (a[i] - m * b[i])_{min}≥ 0
∑(a[i]−m∗b[i])min≥0 ⇔
(
∑
a
[
i
]
∑
b
[
i
]
)
m
i
n
≥
m
(\frac{\sum a[i]}{\sum b[i]})_{min} ≥ m
(∑b[i]∑a[i])min≥m
即
M
i
n
≥
m
Min ≥ m
Min≥m
2.若 存在
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
<
0
\sum (a[i] - m * b[i]) < 0
∑(a[i]−m∗b[i])<0 ⇔
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
m
i
n
<
0
\sum (a[i] - m * b[i])_{min}< 0
∑(a[i]−m∗b[i])min<0 ⇔
(
∑
a
[
i
]
∑
b
[
i
]
)
m
i
n
<
m
(\frac{\sum a[i]}{\sum b[i]})_{min} < m
(∑b[i]∑a[i])min<m
即
M
i
n
<
m
Min < m
Min<m
find_Max
1.若 存在
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
≥
0
\sum (a[i] - m * b[i]) ≥ 0
∑(a[i]−m∗b[i])≥0 ⇔
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
m
a
x
≥
0
\sum (a[i] - m * b[i])_{max}≥ 0
∑(a[i]−m∗b[i])max≥0 ⇔
(
∑
a
[
i
]
∑
b
[
i
]
)
m
a
x
≥
m
(\frac{\sum a[i]}{\sum b[i]})_{max} ≥ m
(∑b[i]∑a[i])max≥m
即
M
a
x
≥
m
Max ≥ m
Max≥m
2.若 任意
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
<
0
\sum (a[i] - m * b[i]) < 0
∑(a[i]−m∗b[i])<0 ⇔
∑
(
a
[
i
]
−
m
∗
b
[
i
]
)
m
a
x
<
0
\sum (a[i] - m * b[i])_{max}< 0
∑(a[i]−m∗b[i])max<0 ⇔
(
∑
a
[
i
]
∑
b
[
i
]
)
m
a
x
<
m
(\frac{\sum a[i]}{\sum b[i]})_{max} < m
(∑b[i]∑a[i])max<m
即
M
a
x
<
m
Max < m
Max<m
二分
综上所诉,我们可以二分查找m
1.当
∑
a
[
i
]
∑
b
[
i
]
≥
m
\frac{\sum a[i]}{\sum b[i]} ≥ m
∑b[i]∑a[i]≥m,即m ≤ {Max or Min},→ l = mid;
2.当
∑
a
[
i
]
∑
b
[
i
]
<
m
\frac{\sum a[i]}{\sum b[i]} < m
∑b[i]∑a[i]<m,即m > {Max or Min},→ r = mid;
348. 沙漠之王
思路:
成本为c, 长度为v
1.题意求生成树的
∑
c
[
i
]
∑
v
[
i
]
\frac{\sum c[i]}{\sum v[i]}
∑v[i]∑c[i]的Min, 不妨设
∑
c
[
i
]
∑
v
[
i
]
=
m
\frac{\sum c[i]}{\sum v[i]} = m
∑v[i]∑c[i]=m
2.若 任意
∑
(
c
[
i
]
−
m
∗
v
[
i
]
)
≥
0
\sum (c[i] - m * v[i]) ≥ 0
∑(c[i]−m∗v[i])≥0
⇔ 以
(
c
[
i
]
−
m
∗
v
[
i
]
)
(c[i] - m * v[i])
(c[i]−m∗v[i])为边权的最小生成树
⇔
∑
(
c
[
i
]
−
m
∗
v
[
i
]
)
m
i
n
≥
0
\sum (c[i] - m * v[i])_{min}≥ 0
∑(c[i]−m∗v[i])min≥0
⇔
(
∑
c
[
i
]
∑
v
[
i
]
)
m
i
n
≥
m
(\frac{\sum c[i]}{\sum v[i]})_{min} ≥ m
(∑v[i]∑c[i])min≥m
即 m 小于等于Min
则 l = mid 反正则 r = mid
3. 稠密图用prim算法
O
(
n
2
)
O(n^2)
O(n2), 用Kruskal
O
(
m
l
o
g
m
)
O(mlogm)
O(mlogm) ,
m
=
n
2
m = n ^ 2
m=n2 会超时
样例输入:
4
0 0 0
0 1 1
1 1 2
1 0 3
0
样例输出:
1.000
代码:
//最优比例生成树
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1005, M = 1e6 + 10, INF = 10000005;
typedef pair<int, int> PII;
int n;
double mm;
double c[N][N], v[N][N], dist[N];
bool st[N];
struct Point{
int x, y, d;
}point[N];
double dis(Point a, Point b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double prim(double m)
{
for(int i = 1; i <= n; i ++ ) dist[i] = INF;
memset(st, false, sizeof st);
double sum = 0;
for(int i = 0; i < n; i ++ )
{
int t = -1;
for(int j = 1; j <= n; j ++ )
if(!st[j] && (t == -1 || dist[j] < dist[t]))
t = j;
if(i) sum += dist[t];
for(int j = 1; j <= n; j ++ )
{
double tmp = c[t][j] - v[t][j] * m;
if(!st[j]) dist[j] = min(dist[j], tmp);
}
st[t] = true;
}
return sum;
}
int main()
{
while(cin >> n)
{
if(n == 0) break;
for(int i = 1; i <= n; i ++ )
{
int x, y, d;
cin >> x >> y >> d;
point[i] = {x, y, d};
}
for(int i = 1; i <= n; i ++ )
for(int j = i + 1; j <= n; j ++ )
{
c[i][j] = c[j][i] = (double)abs(point[i].d - point[j].d);
v[i][j] = v[j][i] = dis(point[i], point[j]);
}
double l = 0, r = 100;
while(r - l >= 1e-4)
{
double mid = (l + r) / 2.0;
if(prim(mid) >= 0) l = mid;
else r = mid;
}
printf("%.3lf\n", r);
}
return 0;
}