T a l e n t S h o w Talent\ Show Talent Show
题目描述见链接 .
正 解 部 分 \color{red}{正解部分} 正解部分
0 / 1 0/1 0/1 分数规划,
二分出答案 m i d mid mid, 要满足 ∑ t i ∑ w i ≤ m i d → m i d × ∑ w i − ∑ t i ≥ 0 → ∑ ( m i d × w i − t i ) ≥ 0 \frac{\sum t_i}{\sum w_i} \le mid \rightarrow mid\times \sum w_i - \sum t_i \geq 0 \rightarrow \sum\left( mid\times w_i - t_i\right) \geq 0 ∑wi∑ti≤mid→mid×∑wi−∑ti≥0→∑(mid×wi−ti)≥0 .
由于题目有总重量不小于 W W W 的限制, 所以不能直接贪心选,
考虑 背包, 设
F
[
i
]
F[i]
F[i] 表示总重量为
i
i
i 所能获得的最大值, 由于
W
W
W 较小, 而
w
i
w_i
wi 比较大,
所以考虑将
F
[
x
≥
W
]
F[x \geq W]
F[x≥W] 全部存在
F
[
W
]
F[W]
F[W] 中, 做
0
/
1
0/1
0/1 背包 即可 .
实 现 部 分 \color{red}{实现部分} 实现部分
#include<bits/stdc++.h>
#define reg register
const int maxn = 1005;
const double inf = 1e10;
const double eps = 1e-9;
int N;
int W;
int w[maxn];
int t[maxn];
double F[maxn];
bool check(double mid){
for(reg int i = 1; i <= W; i ++) F[i] = -inf;
F[0] = 0;
for(reg int i = 1; i <= N; i ++)
for(reg int j = W; j >= 0; j --){
double &tmp = F[std::min(W, j+w[i])];
tmp = std::max(tmp, F[j] + 1.0*t[i] - 1.0*mid*w[i]);
}
return F[W] >= 0;
}
int main(){
scanf("%d%d", &N, &W);
double l = 0, r = 0;
for(reg int i = 1; i <= N; i ++) scanf("%d%d", &w[i], &t[i]), r += t[i];
double Ans = 0;
while(r-l > eps){
double mid = (l+r)/2;
if(check(mid)) Ans = std::max(Ans, mid), l = mid + eps;
else r = mid - eps;
}
printf("%d\n", (int)(Ans*1000));
return 0;
}