题解:洛谷 P4377 [USACO18OPEN] Talent Show G
【题目来源】
洛谷:[P4377 USACO18OPEN] Talent Show G - 洛谷
【题目描述】
Farmer John 要带着他的 \(n\) 头奶牛,方便起见编号为 \(1\ldots n\),到农业展览会上去,参加每年的达牛秀!他的第 \(i\) 头奶牛重量为 \(w_i\),才艺水平为 \(t_i\),两者都是整数。
在到达时,Farmer John 就被今年达牛秀的新规则吓到了:
(一)参加比赛的一组奶牛必须总重量至少为 \(W\)(这是为了确保是强大的队伍在比赛,而不仅是强大的某头奶牛),并且。
(二)总才艺值与总重量的比值最大的一组获得胜利。
FJ 注意到他的所有奶牛的总重量不小于 \(W\),所以他能够派出符合规则(一)的队伍。帮助他确定这样的队伍中能够达到的最佳的才艺与重量的比值。
【输入】
第一行是两个整数,分别表示牛的个数 \(n\) 和总重量限制 \(W\)。
第 \(2\) 到 \((n+1)\) 行,每行两个整数,第 \((i + 1)\) 行的整数表示第 \(i\) 头奶牛的重量 \(w_i\) 和才艺水平 \(t_i\)。
【输出】
请求出 Farmer 用一组总重量最少为 \(W\) 的奶牛最大可能达到的总才艺值与总重量的比值。
如果你的答案是 \(A\),输出 \(1000A\) 向下取整的值,以使得输出是整数(当问题中的数不是一个整数的时候,向下取整操作在向下舍入到整数的时候去除所有小数部分)。
请注意当问题的答案恰好是整数 \(x\) 时,你的程序可能会由于浮点数精度误差问题最后得到一个 \(x-\epsilon\) 的答案,向下取整后变为 \(x-1\) 导致答案错误。这种情况下你可以在输出答案前给答案加上一个极小的值 \(x\gets x+10^{-k}\) 来避免该问题。
【输入样例】
3 15
20 21
10 11
30 31
【输出样例】
1066
【算法标签】
《洛谷 P4377 Talent Show》 #贪心# #背包# #分数规划# #USACO# #2018#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 255; // 最大物品数量
int n, W; // n: 物品数量, W: 背包容量
int w[N], t[N]; // w: 物品重量, t: 物品价值
double f[1005]; // DP数组,f[j]表示容量为j时的最大价值
// 检查函数:判断是否存在解使得单位重量的价值至少为x
bool check(double x)
{
// 初始化DP数组为负无穷
for (int i = 1; i <= W; i++)
f[i] = -1e9;
// 0-1背包动态规划
for (int i = 1; i <= n; i++)
{
for (int j = W; j >= 0; j--)
{
int k = min(W, j + w[i]); // 防止数组越界
f[k] = max(f[k], f[j] + t[i] - x * w[i]); // 状态转移方程
}
}
return f[W] >= 0; // 判断是否存在可行解
}
// 二分查找最大单位价值
double find()
{
double l = 0, r = 1000; // 二分查找范围
// 精度控制循环
while (r - l > 1e-5)
{
double mid = (l + r) / 2;
if (check(mid)) // 如果当前x可行
l = mid; // 尝试更大的x
else
r = mid; // 尝试更小的x
}
return r; // 返回最大值(注意这里是取整,所以返回r)
}
int main()
{
// 输入物品数量和背包容量
cin >> n >> W;
// 输入每个物品的重量和价值
for (int i = 1; i <= n; i++)
cin >> w[i] >> t[i];
// 计算并输出结果(乘以1000后取整)
int ans = int(find() * 1000);
cout << ans << endl;
return 0;
}
// 使用acwing模板二刷
#include <bits/stdc++.h>
using namespace std;
const int N = 255; // 最大物品数量
int n, W; // n: 物品数量, W: 背包容量
int w[N], t[N]; // w: 物品重量数组, t: 物品价值数组
double f[1005]; // DP数组,f[j]表示容量为j时的最大价值
// 检查函数:判断是否存在解使得单位重量的价值至少为x
bool check(double x)
{
// 初始化DP数组为负无穷(表示不可达)
for (int i = 1; i <= W; i++)
f[i] = -1e9;
// 0-1背包动态规划
for (int i = 1; i <= n; i++)
{
// 逆序遍历背包容量
for (int j = W; j >= 0; j--)
{
// 计算放入当前物品后的容量(防止越界)
int k = min(W, j + w[i]);
// 状态转移:比较放入和不放入当前物品的价值
f[k] = max(f[k], f[j] + t[i] - x * w[i]);
}
}
// 判断最终结果是否非负
return f[W] >= 0;
}
// 二分查找函数:寻找最大单位价值
double find()
{
double l = 0, r = 1000; // 二分查找的初始范围
double eps = 1e-5; // 精度控制
// 二分查找循环
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) // 如果当前x可行
l = mid; // 尝试更大的x
else
r = mid; // 尝试更小的x
}
return l; // 返回最大单位价值
}
int main()
{
// 输入物品数量和背包容量
cin >> n >> W;
// 输入每个物品的重量和价值
for (int i = 1; i <= n; i++)
cin >> w[i] >> t[i];
// 计算并输出结果(乘以1000后取整)
int ans = int(find() * 1000);
cout << ans << endl;
return 0;
}
【运行结果】
3 15
20 21
10 11
30 31
1066
浙公网安备 33010602011771号