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;
}
posted @ 2022-03-31 11:15  pbc的成长之路  阅读(42)  评论(0编辑  收藏  举报