给定
n
n
n 个二元组
(
v
a
l
u
e
i
,
c
o
s
t
i
)
(value_i,cost_i)
(valuei,costi),分别表示价值和代价
设
x
i
∈
{
0
,
1
}
x_i∈\{0, 1\}
xi∈{0,1},表示第
i
i
i 个二元组是否选取
要求最大(小)化下式:
r
=
∑
v
a
l
u
e
i
×
x
i
∑
c
o
s
t
i
×
x
i
r = \frac{ \sum{value_i} \times x_i } { \sum{cost_i} \times x_i }
r=∑costi×xi∑valuei×xi
二分
m
i
d
mid
mid ,若最优值
≥
m
i
d
:
≥ mid:
≥mid:
∑
v
a
l
u
e
i
×
x
i
∑
c
o
s
t
i
×
x
i
≥
m
i
d
\frac{ \sum{value_i} \times x_i } { \sum{cost_i} \times x_i } ≥ mid
∑costi×xi∑valuei×xi≥mid
∑
(
v
a
l
u
e
i
−
m
i
d
∗
c
o
s
t
i
)
×
x
i
≥
0
\sum{(value_i - mid * cost_i)} \times x_i ≥ 0
∑(valuei−mid∗costi)×xi≥0
也就是找到一组
{
x
i
}
\{x_i\}
{xi} 使得
∑
(
v
a
l
u
e
i
−
m
i
d
∗
c
o
s
t
i
)
\sum{(value_i - mid * cost_i)}
∑(valuei−mid∗costi) 最大
若找到的最大值
≥
0
≥ 0
≥0,也就是 最优值大于
m
i
d
mid
mid,反之就是小于
m
i
d
mid
mid
详细证明请见01分数规划问题相关算法与题目讲解(二分法与Dinkelbach算法)
例题一:POJ 2976 Dropping tests
选取 n − k n-k n−k 个二元组使得 ∑ a i ∑ b i \frac{ \sum{a_i} } { \sum{b_i} } ∑bi∑ai 最大
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const double eps = 1e-5;
const int maxn = 1e3 + 5;
int n, k, a[maxn], b[maxn];
double c[maxn];
bool ck(double x){
for(int i=1; i<=n; i++) c[i] = a[i] - x * b[i];
sort(c+1, c+1+n);
double res = 0;
for(int i=k+1; i<=n; i++) res += c[i];
return res >= eps;
}
signed main() {
while(scanf("%d%d", &n, &k) && n){
for(int i=1; i<=n; i++) scanf("%d", a+i);
for(int i=1; i<=n; i++) scanf("%d", b+i);
double l = 0, r = 1e5, mid;
while((r - l) >= eps){
mid = (l + r) / 2;
if(ck(mid)) l = mid + eps;
else r = mid - eps;
}
printf("%.0f\n", r * 100);
}
}
例题二:P4322 [JSOI2016]最佳团体
选取
k
k
k 个二元组使得
∑
p
i
∑
s
i
\frac{ \sum{p_i} } { \sum{s_i} }
∑si∑pi 最大
同时选取一个二元组有可能有一个必须先选的依赖二元组
因此二分后,用
n
2
n^2
n2 的树形背包,每个点的点权为
∑
(
p
i
−
m
i
d
∗
s
i
)
\sum{(p_i - mid * s_i)}
∑(pi−mid∗si)
我看题解里有在
d
f
s
dfs
dfs 里
d
p
dp
dp,然后说是
n
2
n^2
n2 的,但是代码却写成
n
3
n^3
n3 。。。
树形背包
n
2
n^2
n2 的写法,合并子树时,必须先
d
p
dp
dp,然后再加上这个子树的
s
i
z
e
size
size
或者在
d
f
s
dfs
dfs 序上
d
p
dp
dp 也可,常数更小
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 2505;
const double eps = 1e-5;
int n, m, s[maxn], p[maxn], r[maxn];
int dfn[maxn], tot, nxt[maxn];
double dp[maxn][maxn], v[maxn];
vector <int> g[maxn];
void dfs(int u){
dfn[u] = tot++;
for(auto v : g[u]) dfs(v);
nxt[dfn[u]] = tot;
}
inline double ck(double x){
for(rint i=1; i<=n+1; i++)
for(rint j=0; j<=m; j++)
dp[i][j] = -1e9;
for(rint i=1; i<=n; i++) v[dfn[i]] = p[i] - s[i] * x;
for(rint i=0; i<=n; i++)
for(rint j=0; j<=min(m, i); j++){
dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j] + v[i]);
dp[nxt[i]][j] = max(dp[nxt[i]][j], dp[i][j]);
}
return dp[n+1][m];
}
signed main() {
scanf("%d%d", &m, &n); m++;
for(rint i=1; i<=n; i++) {
scanf("%d%d%d", s+i, p+i, r+i);
g[r[i]].push_back(i);
}
dfs(0);
double l = 0, r = 1e4, mid;
while((r - l) >= eps){
mid = (l + r) * 0.5;
if(ck(mid) >= eps) l = mid + eps;
else r = mid - eps;
}
printf("%.3f\n", r);
}