首页 > 代码库 > 笔记 (note)

笔记 (note)

笔记
【问题描述】
给定一个长度为m的序列a,下标编号为1~m。序列的每个元素都是1~n的
整数。定义序列的代价为

m?1

∑|ai+1-ai|

i=1

你现在可以选择两个数x和y,并将序列a中所有的x改成y。x可以与y相等。
请求出序列最小可能的代价。
【输入格式】
输入第一行包含两个整数n和m。第二行包含m个空格分隔的整数,代表序
列a。
【输出格式】
输出一行,包含一个整数,代表序列最小的代价。
【样例输入 1】
4 6
1 2 3 4 3 2
【样例输出 1】
3
【样例输入 2】
10 5
9 4 3 8 8
【样例输出 1】
6
【样例解释】
样例 1 中,最优策略为将 4 改成 3。

样例 2 中,最优策略为将 9 改成 4。
【数据规模和约定】
对于30%的数据,n,m<=100.
对于60%的数据,n,m ≤ 2000。
对于100%的数据,1 ≤ n,m ≤ 100,000。

暴力可过6个点 也就是30分。

满分做法是寻找附近的元素,记录贡献值。

代码为满分做法

技术分享
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ios>
#include <vector>
using namespace std;

typedef long long ll;

const int N = (int)1e5;

int n, m, a[N + 1];
vector<int> b[N + 1];

int main(int argc, char *argv[]) {
//    freopen("note.in", "r", stdin);
//    freopen("note.out", "w", stdout);

    ios :: sync_with_stdio(false);//让cin跑快点。(别轻易用)
    cin >> n >> m;
    for (int i = 1; i <= m; ++i) cin >> a[i];
    for (int i = 1; i <= m; ++i) {
        if (i > 1 && a[i - 1] != a[i]) b[a[i - 1]].push_back(a[i]);
        if (i < m && a[i + 1] != a[i]) b[a[i + 1]].push_back(a[i]);
    }//找附近的元素。

    ll ans = 0LL, sum = 0LL;
    for (int i = 1; i <= n; ++i) {
        if (!b[i].size()) continue;
        sort(b[i].begin(), b[i].end());
        int y = b[i][b[i].size() >> 1];
        ll before = 0LL, after = 0LL;
        for (int j = 0; j < b[i].size(); ++j) {
            before += abs(i - b[i][j]);
            after += abs(y - b[i][j]);
        }
        ans = max(ans, before - after), sum += before;
    }

    cout << sum / 2 - ans << endl;//因为加了两边,所以/2。

    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

 

笔记 (note)