CF-Round #635-div2-D题

23 篇文章 0 订阅

CF-Round #635-div2-D题

D. Xenia and Colorful Gems

传送门

这道题排序二分模拟题。

题目大意:
给你三种颜色的宝石,每个宝石有他的重点。主人公会选择每种颜色的宝石各一个。并且要求这三个宝石的重量差的平方和最小。
假设选的三个宝石的重量分别是x, y, z;
我们要使得下面的式子的值最小:
1

题目思路:其实就是模拟,只是模拟的过程需要二分来优化。
我们首先把给的重量分别排序下,
然后一个一个来模拟。
我们需要模拟六次。
两次以红宝石为基础,两次以蓝宝石为基础,两次以绿宝石为基础。
交叉进行。
这里举一个例子:
我们以红宝石为基础时,遍历这个红宝石的重量,找到当前红宝石的重量与绿宝石和蓝宝石最接近的。
我们用lower_bound()找到重量最接近的绿宝石(返回的是第一个大于等于的)
用upper_bound()找到重量最接近的蓝宝石(返回的是第一个大于的)
如果lower_bound()返回的是end,说明绿宝石中没有比当前操作红宝石大的,代表我们选取的红宝石重量太大。
如果upper_bound()返回的是begin,说明蓝宝石中第一个就已经比当前操作红宝石大了,代表我们选取的红宝石重量太轻了。
以上这两种情况直接continue就行。
其余情况
我们把upper_bound()返回的索引-1;(因为返回的是第一个大于,有可能前面一个索引是等于的情况,小于也不要紧。)
然后再维护最小值ans.

这里说一下为什么upper_bound()在判断的时候判断的是begin,而不是end。因为如果我们判断end的话,假如现在出现了蓝宝石的最后一个都不能大于当前操作的红宝石的重量,按照判断end的情况,我们就直接continue了。但是这种情况如果恰好是我们的答案最小值。我们就漏掉了(其他情况也不能到达这种情况。)
举个具体的例子:
红宝石重量:1 2
绿宝石重量:3 4
蓝宝石重量:6 7
现在操作这种情况:绿宝石作为基础,当前操作重量是4
蓝宝石作为lower_bound()的判断
红宝石作为upper_bound()的判断

那么lower_bound()返回的是0,也就是蓝宝石重量为6;(第一个大于等于的)
upper_bound()返回的是end,也就是当前绿宝石的操作重量比红宝石的重量全都大。
按照上面的全部都判断end,这个时候直接continue;
最后我们就会得到错误答案,ans = 26
但实际上答案是24;(恰好就是我们continue的这种情况)
选择绿宝石重量为4,蓝宝石重量为6,红宝石重量为2;

所以我们需要判断begin,我们在后面进行了–操作(所以不需要担心到end会不会越界的问题)
还有一个问题:那么我们跳过begin情况会不会漏掉一些情况呢,是不会漏掉的。因为我们执行这个模拟操作执行六次,每种颜色都会被当成基础来执行。跳过begin的原因是当前操作的宝石重量太小了,跳过的begin,会在某一次模拟中当成基础找到对应的最接近的重量。有lower和upper的同时配合,可以得到最佳答案。

这个地方需要想清楚

代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int nr, nb, ng;
ll ans;

ll square(int a)
{
	return 1ll * a * a;
}

void solve(vector<int> a, vector<int> b, vector<int> c)
{
	int siz = a.size();
	for (int i = 0; i < siz; i++)
	{
		int t1 = lower_bound(b.begin(), b.end(), a[i]) - b.begin();
		int t2 = upper_bound(c.begin(), c.end(), a[i]) - c.begin();
		if (t1 == b.size() || !t2)
		{
			continue;
		}
		t2--;
		ans = min(ans, square(a[i] - b[t1]) + square(a[i] - c[t2]) + square(b[t1] - c[t2]));
	}
}

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		cin >> nr >> ng >> nb;
		vector<int> r(nr);
		vector<int> g(ng);
		vector<int> b(nb);
		for (int i = 0; i < nr; i++)
		{
			scanf ("%d", &r[i]);
		}
		for (int i = 0; i < ng; i++)
		{
			scanf ("%d", &g[i]);
		}
		for (int i = 0; i < nb; i++)
		{
			scanf ("%d", &b[i]);
		}
		sort(r.begin(), r.end());
		sort(g.begin(), g.end());
		sort(b.begin(), b.end());
		ans = 9e18 + 10;
		solve(r, b, g);
		solve(r, g, b);
		solve(b, r, g);
		solve(b, g, r);
		solve(g, r, b);
		solve(g, b, r);
		cout << ans << endl;
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值