V字钩爪(贪心)
题意
有\(n\)堆排列整齐的且相同质量的宝石,每颗宝石具有价值\(v_i\)。
你有一个倒V字的钩爪,该钩爪长度为\(k\),可同时抓取\(i\)和\(i+k\)的两颗宝石(左钩和右钩)。必须左右钩都有宝石,否则会不平衡而脱钩。
任意次抓取,求最终能得到的最大价值是多少?
数据范围
\(1\leq k<n\leq 1e6\)
\(v_i \leq 1e9\)
思路
这道题的思路较为经典,值得学习。
首先我们很容易想到,整个序列可以被划分为\(k\)条链(每个位置对\(k\)取模,模数相同的连成一条练)。抓取的过程中,是在链的内部进行的,不会跨链进行。
对于每条链,如果链中元素个数为偶数,那么就可以全取;
如果链中元素个数为奇数,不妨将链中元素按照\(1,2,3\dots\)进行编号。显然,至少有一个元素取不走。另外分析发现,如果只有一个元素不取,那么这个元素的编号一定是奇数。并且,如果想不取偶数,那么一定有至少一个奇数也取不了。
因此,最优策略就是,除了每一条链中编号为奇数的最小值不取,其他全取。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1000010;
int n, k;
ll mi[N];
int cnt[N];
int main()
{
scanf("%d%d", &n, &k);
ll ans = 0;
memset(mi, 0x3f, sizeof mi);
for(int i = 1; i <= n; i ++) {
ll x;
scanf("%lld", &x);
cnt[i % k] ++;
if(cnt[i % k] % 2) mi[i % k] = min(mi[i % k], x);
ans += x;
}
for(int i = 0; i < k; i ++) {
if(cnt[i] % 2) {
ans -= mi[i];
}
}
printf("%lld\n", ans);
return 0;
}