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;
}
posted @ 2025-07-25 18:23  Terry_RE  阅读(10)  评论(0)    收藏  举报