本文涉及知识点
[USACO21DEC] Closest Cow Wins S
题目描述
Farmer John 沿着一条高速公路拥有一个很长的农场,可以被看作类似于一维数轴。沿着农场有 K K K 块草地( 1 ≤ K ≤ 2 ⋅ 1 0 5 1 \leq K \leq 2\cdot 10^5 1≤K≤2⋅105);第 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 0≤ti≤109)。Farmer John 的死对头 Farmer Nhoj 已经将他的 M M M 头奶牛( 1 ≤ M ≤ 2 ⋅ 1 0 5 1 \leq M \leq 2\cdot 10^5 1≤M≤2⋅105)放在了位置 f 1 … f M f_1 \ldots f_M f1…fM 。所有这些 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 1≤N≤2⋅105)个位置(不一定是整数)放置他的奶牛。这些位置必须与 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++**实现。