CF433C Ryouko's Memory Note(贪心)题解

题意

给出2个正整数 \(n,m(1\leq n,m \leq 10^5)\)与长度为\(m\)的序列\(a[1-m]\),保证\(a[i]\leq n\),你可以把所有值为\(a[i]\)的元素改为\(a[j]\),可以不改且只能改一次,要求最小化\(\sum\limits_{i=1}^{i\leq m-1} |a_i-a_{i+1}|\)

算法

贪心,暴力枚举

思路

首先要知道一个结论:

\(a\)为序列\(b\)的中位数时,\(\sum\limits_{i=1}^{i\leq n}{|a-b_i|}\)最小。

放到这个题目里,对于每一个元素\(a[i]\),序列\(b\)就是与它相邻且不相等的元素集合。所以,我们可以统计出与每一个相邻的数,排序之后用中位数计算对答案的贡献。

由于每一个数最多被枚举了2遍,所以总复杂度为\(O(2mlogm)\)

具体实现请看代码。

参考代码

#include <cstdio>
#include <algorithm>
#include <vector>
#define LL long long

using namespace std;

const int maxn = 1e5 + 10;
int n,a[maxn],m;
LL Ans = 0;
vector<int> v[maxn];

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; ++ i) scanf("%d", a + i);
    for(int i = 1; i <= m; ++ i){
        if(a[i] != a[i - 1] && i != 1) v[a[i]].push_back(a[i - 1]);
        if(a[i] != a[i + 1] && i != m) v[a[i]].push_back(a[i + 1]);
        if(i != 1) Ans += abs(a[i] - a[i - 1]);  //先处理出初始答案
    }
    LL ls = Ans;
    for(int i = 1; i <= n; ++ i){
        int len = v[i].size();
        if(!len) continue;
        sort(v[i].begin(), v[i].end());
        int pos = v[i][len / 2]; LL ret = ls;
        for(int j = 0; j < len; ++ j){
            ret -= abs(i - v[i][j]);
            ret += abs(pos - v[i][j]); //计算更改后的答案
        }
        Ans = min(Ans, ret);
    }
    printf("%lld\n", Ans);
    return 0;
}
posted @ 2020-11-26 19:42  When_C  阅读(138)  评论(0编辑  收藏  举报