【C++贪心 滑动窗口】P7990 [USACO21DEC] Closest Cow Wins S|省选-

本文涉及知识点

C++贪心
C++算法:滑动窗口及双指针总结

[USACO21DEC] Closest Cow Wins S

题目描述

Farmer John 沿着一条高速公路拥有一个很长的农场,可以被看作类似于一维数轴。沿着农场有 K K K 块草地( 1 ≤ K ≤ 2 ⋅ 1 0 5 1 \leq K \leq 2\cdot 10^5 1K2105);第 i i i 块草地位于位置 p i p_i pi 并具有美味值 t i t_i ti 0 ≤ t i ≤ 1 0 9 0\le t_i\le 10^9 0ti109)。Farmer John 的死对头 Farmer Nhoj 已经将他的 M M M 头奶牛( 1 ≤ M ≤ 2 ⋅ 1 0 5 1 \leq M \leq 2\cdot 10^5 1M2105)放在了位置 f 1 … f M f_1 \ldots f_M f1fM 。所有这些 K + M K+M K+M 个位置均是 [ 0 , 1 0 9 ] [0,10^9] [0,109] 范围内的不同整数。

Farmer John 需要选择 N N N 1 ≤ N ≤ 2 ⋅ 1 0 5 1\le N\le 2\cdot 10^5 1N2105)个位置(不一定是整数)放置他的奶牛。这些位置必须与 Farmer Nhoj 的奶牛已经占用的位置不同,但是 Farmer John 可以将他的奶牛放在与草地相同的位置。

拥有最靠近某个草地的奶牛的农夫拥有这一草地。如果来自两方农夫的两头奶牛距这一草地相等,则 Farmer Nhoj 拥有该草地。

给定 Farmer Nhoj 的奶牛的位置以及草地的位置和美味值,求 Farmer John 的奶牛以最优方式放置时可以达到的最大总美味值。

输入格式

输入的第一行包含 K K K M M M N N N

以下 K K K 行每行包含两个空格分隔的整数 p i p_i pi t i t_i ti

以下 M M M 行每行包含一个整数 f i f_i fi

输出格式

输出一个整数,表示最大总美味值。注意这个问题的答案可能无法用 32 位整数型存储,你可能需要使用 64 位整数型(例如,C 或 C++ 中的 “long long”)。

样例 #1

样例输入 #1

6 5 2
0 4
4 6
8 10
10 8
12 12
13 14
2
3
5
7
11

样例输出 #1

36

提示

【样例解释】

如果 Farmer John 将奶牛放在位置 11.5 11.5 11.5 8 8 8 则他可以得到总美味值 10 + 12 + 14 = 36 10+12+14=36 10+12+14=36

贪心 决策包容性

死对头的M头奶牛,将整个农场分为M+1个区间。
性质一:一头奶牛只能占据一个区间的部分或全部农场,不能同时占领2个区域。两头奶牛,可以占领一个完整区域。
第0个区间,只需要一头奶牛在m0-0.0001处就可以占据。
第M个区间,将一头奶牛放到mm+0.0001处就可以占据。
其它区间,需要两头奶牛完全占据,m i _i i+0.0001和m i + 1 _{i+1} i+1-0.0001处各放一头奶牛。
其它区间,长度为len,一头奶牛可以占据长度len/2的区间,只能是左闭右开空间或左开右闭空间。如果len是偶数,能占据任意len/2个连续正数;如果是len是奇数,可以占据len/2+1个连续整数。即(len+1)/2个连续整数。
性质二:一头奶可以占据任何区域的任意半个区间,x1个区间有一头奶牛,x2个区间有两头奶牛。则x1+2x2=N。
令f(i)是一头牛占据i区域的最大美味度,g(i)是两头奶牛占据的最大美味度。
大根堆noSel记录没有被完全占领的区域(0头牛或1头牛),派一头牛增加的美味度。
初始:记录所有f(i),f(i)出堆后,g(i)-f(i)入堆。g(i)-f(i)出堆后,不入堆。
noSel为空或出堆次数到达N结束。
证明过程:令按此方法求的解是方案A。任何解的任意步骤,如果候选数据相同,则一定不优于A。那会不会某步让一个优秀的解可选,下一步选择。比如:{5,3},{4,2},{1,9999}。显然A方案{5,4},劣于{1,9999}。
性质三:f(i) >= g(i)-f(i)。任意一个区间,一头牛可以占据左半区间(左开右闭),也可以占据右半空间(左闭右开)。故f(i) >= 这两个半空间的最大者。
x1>=f(i),x2>=f(i) ,f(i) >= g(i)-f(i),即x1+x2 >=g(i)。故这种可能不存在。
总结:不在候选列表的数据一定不优于候选列表的最大值。如果g(i)-f(i)不在候选列表,则f(i)一定在候选列表,结合性质一,结论得证。
向量pa记录农场的位置及美味度,按位置升序排序。向量vm记录死对头牛的位置,升序排序。
M记录死对头牛的数量,vm增加2e9。
将pa拆分到各区间vr,从小到大处理vr[i]。
pa的位置小于vm[i],则放到vr[i]。
g(i)等于vr[i]美味度之和。f(0)=g(0) f(M)=g(M),其它:
利用滑动窗口求f(i)

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>

#include <bitset>
using namespace std;



template<class T = int>
vector<T> Read(int n,const char* pFormat = "%d") {
	vector<T> ret(n);
	for(int i=0;i<n;i++) {
		scanf(pFormat, &ret[i]);	
	}
	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 T1,class T2>
void ReadTo(pair<T1, T2>& pr) {
	cin >> pr.first >> pr.second;
}

class Solution {
public:
	long long Ans(vector<pair<int, int>>& pa, vector<int>& vm, int n)
	{
		const int M = vm.size();
		sort(pa.begin(), pa.end());
		sort(vm.begin(), vm.end());
		vm.emplace_back((int)2e9);
		vector<vector<pair<int, int>>> vr(M + 1);
		for (int i = 0, j = 0; i <= M; i++) {
			while ((j < pa.size()) && (pa[j].first < vm[i])) {
				vr[i].emplace_back(pa[j]); j++;
			}
		}
		vector<long long> f(M + 1), g(M + 1);
		for (int i = 0; i <= M; i++) {
			const auto& v = vr[i];
			for (const auto& [tmp, a] : v) {
				g[i] += a;
			}
			if ((0 == i) || (M == i)) { f[i] = g[i]; continue; }
			const int len = (vm[i] - vm[i - 1] + 1) / 2;
			long long cur = 0;
			for (int left = 0, r = 0; left < v.size(); left++) {
				while ((r < v.size()) && (v[r].first < v[left].first + len)) {
					cur += v[r].second; r++;
				}
				f[i] = max(f[i], cur);
				cur -= v[left].second;
			}
		}
		priority_queue<pair<long long, int>> maxHeap;
		for (int i = 0; i <= M; i++) {
			maxHeap.emplace(f[i], i);
		}
		long long ans = 0;
		while (n && maxHeap.size()) {
			auto [a, inx] = maxHeap.top();
			maxHeap.pop();
			ans += a;
			if (-1 != inx) { maxHeap.emplace(g[inx] - f[inx], -1); }
			n--;
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int k,m,n;
	scanf("%d%d%d", &k,&m,&n);
	vector<pair<int, int>> pa(k);
	for (int i = 0; i < k; i++) {
		scanf("%d%d", &pa[i].first, &pa[i].second);
	}
	auto vm = Read<int>(m);
	auto res = Solution().Ans(pa,vm,n);
#ifdef _DEBUG			
	/*	Out(pa, "pa=");
		Out(vm, ",vm=");	
		printf("n=%d", n);*/
#endif		
	cout << res << std::endl;		
	return 0;
}

单元测试

	vector<pair<int, int>>pa;
		vector<int> vm;
		int n;
		TEST_METHOD(TestMethod11)
		{
			pa = { {0,4},{4,6},{8,10},{10,8},{12,12},{13,14} }, vm = { 2,3,5,7,11 },n = 2;
			auto res = Solution().Ans(pa,vm,n);
			AssertEx(36LL, 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++**实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

软件架构师何志丹

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

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

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

打赏作者

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

抵扣说明:

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

余额充值