【贪心+二分+双指针】P9559 [SDCPC2023] Fast and Fat|普及

本文涉及知识点

本博文代码打包下载
C++贪心
C++二分查找
C++算法:滑动窗口及双指针总结

[SDCPC2023] Fast and Fat

题面翻译

【题目描述】

您正在参加一场团体越野比赛。您的队伍共有 n n n 名队员,其中第 i i i 名队员的速度为 v i v_i vi,体重为 w i w_i wi

比赛允许每名队员独立行动,也允许一名队员背着另一名队员一起行动。当队员 i i i 背着队员 j j j 时,如果队员 i i i 的体重大于等于队员 j j j,则队员 i i i 的移动速度不会变化,仍然为 v i v_i vi;如果队员 i i i 的体重小于队员 j j j,则队员 i i i 的移动速度会减去两者的体重差值,即变为 v i − ( w j − w i ) v_i - (w_j - w_i) vi(wjwi)。如果队员 i i i 的移动速度将变为负数,则队员 i i i 无法背起队员 j j j。每名队员最多只能背负另一名队员,被背负的队员无法同时背负其他队员。

所有未被背负的队员中,最慢的队员的速度,即为整个队伍的速度。求整个队伍能达到的最大速度。

【输入格式】

有多组测试数据。第一行输入一个整数 T T T 表示测试数据组数,对于每组测试数据:

第一行输入一个整数 n n n 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105)表示队员人数。

对于接下来 n n n 行,第 i i i 行输入两个整数 v i v_i vi w i w_i wi 1 ≤ v i , w i ≤ 1 0 9 1 \le v_i, w_i \le 10^9 1vi,wi109)表示第 i i i 名队员的速度和体重。

保证所有数据中 n n n 之和不超过 1 0 5 10^5 105

【输出格式】

每组数据输出一个整数,表示整个队伍可以达到的最大速度。

【样例解释】

样例数据的最优策略如下:

  • 队员 1 1 1 背起队员 4 4 4。因为 w 1 > w 4 w_1 > w_4 w1>w4,因此队员 1 1 1 速度不变,仍然为 10 10 10
  • 队员 3 3 3 背起队员 2 2 2。因为 w 3 < w 2 w_3 < w_2 w3<w2,因此队员 3 3 3 的速度减少 w 2 − w 3 = 2 w_2 - w_3 = 2 w2w3=2,即速度变为 10 − 2 = 8 10 - 2 = 8 102=8
  • 队员 5 5 5 独立行动,速度为 9 9 9

因此答案为 8 8 8

样例 #1

样例输入 #1

2
5
10 5
1 102
10 100
7 4
9 50
2
1 100
10 1

样例输出 #1

8
1

提示

The optimal strategy for the sample test case is shown as follows:

  • Let member 1 1 1 carry member 4 4 4. As w 1 > w 4 w_1 > w_4 w1>w4, member 1 1 1’s speed remains unchanged, which is still 10 10 10.
  • Let member 3 3 3 carry member 2 2 2. As w 3 < w 2 w_3 < w_2 w3<w2, member 3 3 3’s speed will decrease by w 2 − w 3 = 2 w_2 - w_3 = 2 w2w3=2 and becomes 10 − 2 = 8 10 - 2 = 8 102=8.
  • Member 5 5 5 shall move alone. His/Her speed is 9 9 9.

So the answer is 8 8 8.

贪心+二分+双指针

二分类型:寻找尾端
参数范围:[最小速度,最大速度]
Check函数:能否让所有队员速度都大于等于mid,速度小于mid的队员需要人背,且背后的速度大于等于mid。
令x1是速度不达标,体重最轻的队员。则背他的队友一定是:速度达标,且速度 + 体重 - x1体重 >= mid,如果有多个符合,取速度+体重最小的。不符合的队友,不能背任何队友,故淘汰。
v1记录所有队友的体重和速度,升序。
v2记录所有队员的速度+体重,速度。
j=0
for i1,i2 v1
如果速度大于等于mid忽略
寻找 v2[j]的速度大于等于mid,速度 + 体重 - x1体重 >= mid。如果找不到,返回false
由于j不复位,故Check函数的时间负重度是O(n)。
时间复杂度:O(nlongn)

代码

核心代码

#include 
#include 
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include 
#include 

#include 
using namespace std;



template<class T = int>
vector<T> Read(int n,const char* pFormat = "%d") {
	vector<T> ret;
	T d ;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

template<class T = int>
vector<T> Read( const char* pFormat = "%d") {
	int n;
	scanf("%d", &n);
	vector<T> ret;
	T d;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

string ReadChar(int n) {
	string str;
	char ch;
	while (n--) {
		do
		{
			scanf("%c", &ch);
		} while (('\n' == ch));
			str += ch;
	}
	return str;
}

template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex) :m_iMin(iMinIndex), m_iMax(iMaxIndex) {}
	template<class _Pr>
	INDEX_TYPE FindFrist(_Pr pr)
	{
		auto left = m_iMin - 1;
		auto rightInclue = m_iMax;
		while (rightInclue - left > 1)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd(_Pr pr)
	{
		int leftInclude = m_iMin;
		int right = m_iMax + 1;
		while (right - leftInclude > 1)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax;
};


class Solution {
public:
	int MaxV(vector<int>& v, vector<int>& w) {
		const int N = v.size();
		vector<pair<int, int>> v1, v2;
		for (int i = 0; i < N; i++) {
			v1.emplace_back(w[i], v[i]);
			v2.emplace_back(w[i] + v[i], v[i]);
		}
		sort(v1.begin(), v1.end());
		sort(v2.begin(), v2.end());
		auto Check = [&](int mid) {
			for (int iii = 0, j = 0; iii < N; iii++) {
				if (v1[iii].second >= mid) { continue; }
				bool can = false;
				for (; j < N; j++) {
					if (v2[j].second < mid) { continue; }
					if (v2[j].first - v1[iii].first >= mid) { can = true;  break; }
				}
				if (!can) { return false; }
				j++;
			}
			return true;
		};
		const int iMax = *max_element(v.begin(), v.end());
		CBinarySearch bs(0, iMax);
		return bs.FindEnd(Check);
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int T = 0;
	scanf("%d", &T);
	for (int i = 0; i < T; i++)
	{
		int n;
		scanf("%d", &n);
		vector<int> v, w;
		int t1, t2;
		for (int j = 0; j < n; j++) {
			scanf("%d%d", &t1, &t2);
			v.emplace_back(t1);
			w.emplace_back(t2);
		}
		auto res = Solution().MaxV(v, w);
		cout << res << std::endl;
	}
	return 0;
}

单元测试

vector<int> v,  w;
		TEST_METHOD(TestMethod11)
		{
			v = { 10,1,10,7,9 }, w = { 5,102,100,4,50 };
			auto res = Solution().MaxV(v, w);
			AssertEx(8, res);
		}
		TEST_METHOD(TestMethod12)
		{
			v = { 1,10}, w = { 100,1 };
			auto res = Solution().MaxV(v, w);
			AssertEx(1, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

你可能感兴趣的:(#,洛谷普及,c++,洛谷,算法,贪心,二分查找,双指针,队员)