P4377 [USACO18OPEN] Talent Show G 题解
题意
\(\quad\) 找到最大的 \(\lfloor{\sum\frac{t_i}{w_i}} \times 1000 \rfloor\) 其中 \(\sum w_i \ge W\)。
思路
\(\quad\) 看到"最大的 \(\sum{\frac{a_i}{b_i}}\)"很容易想到分数规划,直接套个板子即可。但这道题相较于板子多了个条件,所以我们引入背包 DP 的思想:在 check 函数中用 01 背包解决 \(\sum w_i \ge W\) 的条件,返回就变成了 \(dp_W \ge 0\)。但这里要注意的是对于这种 \(w_i\) 很大但 \(W\) 很小的 01 背包不能用传统的填表法实现,要用刷表法,不然会 T 飞。
小技巧
\(\quad\) 我们可以在求解前把 \(t_i\) 先乘上 \(1000\),这样既可以避免浮点数运算带来的误差与时耗,还可以避免浮点数二分更高的时间复杂度。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 300;
int n, W;
ll w[N], t[N];
ll dp[N];
bool check(int mid){
for(int i(1); i <= W; ++i)
dp[i] = -1e7;
dp[0] = 0;
for(int i(1); i <= n; ++i)
for(int j(W); j >= 0; --j)
if(j + w[i] >= W)
dp[W] = max(dp[W], dp[j] + t[i] - w[i] * mid);
else
dp[j + w[i]] = max(dp[j + w[i]], dp[j] + t[i] - w[i] * mid);
return dp[W] >= 0;
}
int main(){
cin.tie(0) -> ios::sync_with_stdio(false);
cin >> n >> W;
for(int i(1); i <= n; ++i)
cin >> w[i] >> t[i], t[i] *= 1000;
int l(0), r(1000000);
while(l <= r){
int mid(l+r>>1);
if(check(mid))
l = mid + 1;
else
r = mid - 1;
}
cout << l - 1;
return 0;
}

浙公网安备 33010602011771号