题意
给定n道菜,每道菜有两种烹饪方式(对应a类调料料和b类调料),美味度分别为ai和bi。
给定m个查询,给定x和y,只允许购买x的整数倍a类调料和,y的整数倍的b类调料。
问能否刚好购买这些调料,使得x * x0 + y * y0 == n,且使得n道菜的美味度最大化。
如果不能刚好购买,输出-1;如果能刚好购买,输出最大的美味度。
思路
扩展欧几里得,求出所有解集,然后在所有解集里边,找出离最佳答案最佳的2组。
最佳答案可以通过预处理计算得出。
详见代码。
官方代码
#include <bits/stdc++.h>
#define forn(i, n) for (int i = 0; i < int(n); i++)
using namespace std;
#define ll long long
// 扩展欧几里得 d = gcd(a, b), a * x + b * y == d
ll gcd(ll a, ll b, ll& x, ll& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll x1, y1;
ll d = gcd(b, a % b, x1, y1);
x = y1;
y = x1 - y1 * (a / b);
return d;
}
// a * x0 + b * y0 == c, g = gcd(a, b)
bool find_any_solution(ll a, ll b, ll c, ll &x0, ll &y0, ll &g) {
g = gcd(abs(a), abs(b), x0, y0);
if (c % g) {
return false;
}
x0 *= c / g;
y0 *= c / g;
if (a < 0) x0 = -x0;
if (b < 0) y0 = -y0;
return true;
}
int main() {
int n;
scanf("%d", &n);
vector<int> a(n), b(n);
forn(i, n) scanf("%d%d", &a[i], &b[i]);
// 全部选择b类烹饪方式的美味度
ll cur = accumulate(b.begin(), b.end(), 0ll);
vector<int> difs(n);
forn(i, n) difs[i] = a[i] - b[i];
sort(difs.begin(), difs.end(), greater<int>()); // 降序排序
// bst[i]表示选择 i个a类烹饪方式,n-i个b类烹饪方式 的最大值
vector<ll> bst(n + 1);
forn(i, n + 1){
bst[i] = cur;
if (i < n)
cur += difs[i];
}
// mx表示最优的烹饪方式,最大值 对应的下标位置
int mx = max_element(bst.begin(), bst.end()) - bst.begin();
int m;
scanf("%d", &m);
forn(_, m){
int x, y;
scanf("%d%d", &x, &y);
ll x0, y0, g; // g = gcd(x,y)
if (!find_any_solution(x, y, n, x0, y0, g)) { // 无解的场景
puts("-1");
continue;
}
ll lcm = x * 1ll * y / g;
ll red = x0 * 1ll * x; // 可行解 x0 * x, 任意解 x0 * x + k * lcm
// 找到第一个 >= mx的 下标red
red += (max(0ll, mx - red) + lcm - 1) / lcm * lcm;
red -= max(0ll, red - mx) / lcm * lcm;
// 从任意解集中,选择离mx下标最近的2组,取两者的最大值
ll ans = -1;
if (red <= n) ans = max(ans, bst[red]);
red -= lcm;
if (red >= 0) ans = max(ans, bst[red]);
printf("%lld\n", ans);
}
return 0;
}