题目大意:
有
2
2
个队伍分别选人,一共有个人,
1
1
队选个人,
2
2
队选个人,每一个成员给出在
1
1
队的好影响以及不良影响
C1[i]
C
1
[
i
]
,在
2
2
队的好影响以及不良影响
C2[i]
C
2
[
i
]
,每个人最多只能进入一个队伍,问最后是2个队的
Q
Q
总和与总和的比例最大,即让
SumQSumC
S
u
m
Q
S
u
m
C
最大,输出最大比例保留
6
6
位小数。
分析:
我们可以发现这题可以进行对答案的二分,
即二分区间
[10−6..2000]
[
10
−
6
.
.2000
]
,
但是因为避免精度的误差,
我选择的是二分
[10−8..2000]
[
10
−
8
.
.2000
]
然后我们当前二分到的答案,设为
x
x
,
则,我们要判断是否可行,
显然当
x
x
满足,
那么,我们化简一下,
约掉分母,
ΣQ1[i]+ΣQ2[j]≥(ΣC1[i]+ΣC2[j])x
Σ
Q
1
[
i
]
+
Σ
Q
2
[
j
]
≥
(
Σ
C
1
[
i
]
+
Σ
C
2
[
j
]
)
x
然后去括号,
ΣQ1[i]+ΣQ2[j]≥ΣC1[i]∗x+ΣC2[j]∗x
Σ
Q
1
[
i
]
+
Σ
Q
2
[
j
]
≥
Σ
C
1
[
i
]
∗
x
+
Σ
C
2
[
j
]
∗
x
移项得到:
(ΣQ1[i]−ΣC1[i]∗x)+(ΣQ2[j]−ΣC2[j]∗x)≥0
(
Σ
Q
1
[
i
]
−
Σ
C
1
[
i
]
∗
x
)
+
(
Σ
Q
2
[
j
]
−
Σ
C
2
[
j
]
∗
x
)
≥
0
那么我们知道,对于第
i
i
个人而言,如果要选择,
那么选择,
Q2[i]−Q2[i]∗x
Q
2
[
i
]
−
Q
2
[
i
]
∗
x
较大的那一个,是更优的
那么每次我们设
a[i]
a
[
i
]
表示
Q1[i]−C1[i]∗x
Q
1
[
i
]
−
C
1
[
i
]
∗
x
b[i]
b
[
i
]
表示
Q2[i]−C2[i]∗x
Q
2
[
i
]
−
C
2
[
i
]
∗
x
c[i]
c
[
i
]
表示
a[i]−b[i]
a
[
i
]
−
b
[
i
]
那么显然我们可以发现,当
c[i]
c
[
i
]
排序后,为一个递减序列的时候(即从大到小排列),
那么此时前面的选
1
1
队比选队的更优,后面的选
2
2
队比选队更优
此时我们设
f[i][j]
f
[
i
]
[
j
]
表示前
i
i
个人选了个人可以得到的最大
(ΣQ1[]−ΣC1[]∗x)
(
Σ
Q
1
[
]
−
Σ
C
1
[
]
∗
x
)
g[i][j]
g
[
i
]
[
j
]
表示后面的人
[i..n]
[
i
.
.
n
]
选了
j
j
个人可以得到的最大
那么当存在任意
f[i][n1]+g[i+1][n2]
f
[
i
]
[
n
1
]
+
g
[
i
+
1
]
[
n
2
]
≥0
≥
0
,
则此时的
x
x
<script type="math/tex" id="MathJax-Element-7181">x</script>是有效的,继续向后二分,否则向前
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 505
using namespace std;
struct Node {
double x1, x2, cd;
}a[N];
double f[N][N], g[N][N], Q1[N], C1[N], Q2[N], C2[N];
int n, n1, n2;
bool cmp(Node aa, Node bb) {
return aa.cd > bb.cd;
}
bool Check(double x) {
for (int i = 1; i <= n; i++) {
a[i].x1 = Q1[i] - C1[i] * x;
a[i].x2 = Q2[i] - C2[i] * x;
a[i].cd = a[i].x1 - a[i].x2;
}
sort(a + 1, a + n + 1, cmp);
for (int i = 0; i <= n+1; i++) {
f[i][0] = g[i][0] = 0;
for (int j = 1; j <= n1; j++) f[i][j] = -1e9;
for (int j = 1; j <= n2; j++) g[i][j] = -1e9;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= min(i, n1); j++) f[i][j] = max(f[i-1][j], f[i-1][j-1] + (a[i].x1));
for (int i = n; i >= 1; i--)
for (int j = 1; j <= min(n-i+1, n2); j++) g[i][j] = max(g[i+1][j], g[i+1][j-1] + (a[i].x2));
for (int i = n1; i <= n-n2; i++)
if (f[i][n1] + g[i+1][n2] >= 0) return 1;
return 0;
}
int main() {
freopen("love.in","r",stdin);
freopen("love.out","w",stdout);
scanf("%d %d %d", &n, &n1, &n2);
for (int i = 1; i <= n; i++)
scanf("%lf %lf %lf %lf", &Q1[i], &C1[i], &Q2[i], &C2[i]);
double l = 1e-8, r = 2000, ans = 0;
while (l <= r) {
double mid = (l + r) / 2;
if (Check(mid)) ans = mid, l = mid + 1e-8;
else r = mid - 1e-8;
}
printf("%.6lf\n", ans);
return 0;
}