E. Red-Black Pepper(数论/扩展欧几里得)

题目
参考

题意

给定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;
}
不使用LINQ查询和操作集合 改进代码 namespace SandwichCalories { class Program { static void Main(string[] args) { // sandwich ingredients and their associated calories Dictionary<string, int> ingredients = new Dictionary<string, int>() { { "Bread", 66 }, { "Ham", 72 }, { "Bologna", 57 }, { "Chicken", 17 }, { "Corned Beef", 53 }, { "Salami", 40 }, { "Cheese, American", 104 }, { "Cheese, Cheddar", 113 }, { "Cheese, Havarti", 105 }, { "Mayonnaise", 94 }, { "Mustard", 10 }, { "Butter", 102 }, { "Garlic Aioli", 100 }, { "Sriracha", 15 }, { "Dressing, Ranch", 73 }, { "Dressing, 1000 Island", 59 }, { "Lettuce", 5 }, { "Tomato", 4 }, { "Cucumber", 4 }, { "Banana Pepper", 10 }, { "Green Pepper", 3 }, { "Red Onion", 6 }, { "Spinach", 7 }, { "Avocado", 64 } }; // prompt user for calorie range Console.Write("Enter minimum calories: "); int min_calories = int.Parse(Console.ReadLine()); Console.Write("Enter maximum calories: "); int max_calories = int.Parse(Console.ReadLine()); // calculate the minimum and maximum calories for the sandwich int min_sandwich_calories = 2 * ingredients["Bread"] + ingredients.Values.Min() * 2; int max_sandwich_calories = 2 * ingredients["Bread"] + ingredients.Values.Max() * 2; // check if the calorie range is valid if (max_calories < min_sandwich_calories) { Console.WriteLine("Sorry, it is impossible to create a sandwich within the given calorie range."); } else { // create the sandwich List<string> sandwich = new List<string> { "Bread" }; int sandwich_calories = 1 * ingredients["Bread"]; while (sandwich_calories < min_calories) { // add random ingredient string ingredient = ingredients.Keys.ElementAt(new Random().Next(ingredients.Count)); sandwich.Add(ingredient); sandwich_calories += ingredients[ingredient]; } while (sandwich_calories <= max_calories) { // add random ingredient string ingredient = ingredients.Keys.ElementAt(new Random().Next(ingredients.Count)); // check if the ingredient is the same as the previous one if (sandwich.Count >= 3 && ingredient == sandwich[sandwich.Count - 2]) { continue; } sandwich.Add(ingredient); sandwich_calories += ingredients[ingredient]; // check if the sandwich is already at the maximum calorie limit if (sandwich_calories == max_sandwich_calories) { break; } } // add the last slice of bread sandwich.Add("Bread"); // print the sandwich and its total calories Console.WriteLine("Your sandwich: " + string.Join(", ", sandwich)); Console.WriteLine("Total calories: " + sandwich_calories); } } } }
最新发布
06-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值