A8-3:混合实验
2026-05-10 13:20:09
发布于:浙江
5阅读
0回复
0点赞
第 3 题 混合实验
题意分析
有 个物体,第 个物体含有 质量的 A 元素和 质量的 B 元素,选取代价为 。
需要从 个物体中选出至少一个,混合后满足:
- A 元素总质量 与 B 元素总质量 的比例恰好等于
- 在所有满足比例的方案中,总代价 最小
如果不存在可行方案,输出 。
关键约束:
- ,
- ,
由于 ,,所以 的最大可能值都不超过 。值域非常小,适合做二维 0/1 背包 DP。
思路分析
状态设计
设 表示:使得 A 元素总和恰好为 、B 元素总和恰好为 时的最小代价。
- 初始状态:(什么都不选,代价为 0)
- 其他状态初始化为 (不可达)
状态转移(0/1 背包,复制新表法)
对每个物品 ,做选/不选的决策:
- 不选: 继承自 (已通过
auto ndp = dp;复制) - 选:若 可达,则
每轮处理完一个物品后,把 ndp 赋回 dp,进入下一轮。这种先复制再更新的写法天然避免了同一物品被重复选取。
答案提取
由于 ,比例 当且仅当存在正整数 使 、。
枚举正整数 ,检查 ,取最小值。如果都不可达,输出 。
复杂度
- 状态数:
- 总时间:
- 完全可以在 1 秒内跑完
代码实现(逐行解释)
#include<bits/stdc++.h>
using namespace std;
int main() {
int N, Ma, Mb;
cin >> N >> Ma >> Mb;
// 读入每个物品的 (a, b, c)
vector<int> a(N), b(N), c(N);
for(int i = 0; i < N; ++i)
cin >> a[i] >> b[i] >> c[i];
// A、B 总和上限:N=40, a_i/b_i ≤ 10,所以最大 = 400
const int max_sum = 400;
// dp[i][j] 表示 A 总和=i、B 总和=j 时的最小代价
// 初始化全为 INT_MAX 表示"不可达"
vector<vector<int>> dp(max_sum + 1, vector<int>(max_sum + 1, INT_MAX));
// 初始状态:什么都不选,A=0、B=0,代价为 0
dp[0][0] = 0;
// 逐个物品做 0/1 背包
for(int k = 0; k < N; ++k) {
// 关键:把整个 dp 复制一份到 ndp
// 接下来"从 dp 读、向 ndp 写",保证每个物品最多被选一次
auto ndp = dp;
// 枚举所有当前可达的 (i, j) 状态
for(int i = 0; i <= max_sum; ++i) {
for(int j = 0; j <= max_sum; ++j) {
// 只有当前状态可达,才能基于它转移
if(dp[i][j] != INT_MAX) {
// 选第 k 个物品后到达的新状态
int ni = i + a[k];
int nj = j + b[k];
// 不能超出数组上限
if(ni <= max_sum && nj <= max_sum) {
// 更新新状态的最小代价
ndp[ni][nj] = min(ndp[ni][nj], dp[i][j] + c[k]);
}
}
}
}
// 用更新后的 ndp 替换旧 dp,进入下一轮
dp = ndp;
}
// 在所有满足比例的状态中找最小代价
// 因为 gcd(Ma, Mb) = 1,所以满足 sa:sb = Ma:Mb 的状态 ⇔ sa = k*Ma, sb = k*Mb
int ans = INT_MAX;
for(int k = 1; k * Ma <= max_sum && k * Mb <= max_sum; ++k) {
if(dp[k * Ma][k * Mb] < ans) {
ans = dp[k * Ma][k * Mb];
}
}
// 如果没有任何可达状态,输出 -1,否则输出最小代价
cout << (ans != INT_MAX ? ans : -1);
return 0;
}
这里空空如也


有帮助,赞一个