【考前模拟】笔记 (不知道神魔算法)

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

m-1
∑|a[i+1]-a[i]|
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。
测试题 #4 笔记
第 3 页 共 6 页
【数据规模和约定】
31。
60%的数据,?,? ≤ 2000。
对于100%的数据,1 ≤ ?,? ≤ 100,000。

 

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ios>
#include <vector>
using namespace std;
typedef long long ll;//typedef类性定义,目的是为了使源代码更易于阅读和理解; 
const int N=(int)1e5;
int n, m, a[N+1];
vector<int>b[N+1];//定义二维动态数组b; 
int main(/*int argc, char *argv[]*/){
    //freopen("note.in", "r", stdin);
    //freopen("note.out", "w", stdout);
    ios::sync_with_stdio(false);/*读入优化,使 cin快于scanf;*/ 
    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]);
    }/*把与a[i]相邻的点记录在以a[i]为标志的数组中 */
    ll ans=0LL, sum=0LL;/*定义ans,sum是long long类型的;*/  
    for(int i=1;i<=n;++i){/*枚举从1到n所有的点;*/ 
        if(!b[i].size()) 
            continue;/*如果该点没有相邻的点,
    那么该点不在数组a中,继续下个循环*/ 
        sort(b[i].begin(),b[i].end());/* 对于他相邻得点进行排序*/ 
        int y=b[i][b[i].size()>>1];/*找出其中的中位数;*/ 
        ll before=0LL,after=0LL;/*定义before,after是long long类型的,
    before用于记录修改前的该点与其相邻点的和值,
    after用于记录该点修改后与其相邻点的和值;*/ 
        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/*这个用于记录该点的变化值*/);/*用ans记录所能减小的最大值*/ 
        sum+=before;/*sum用于存储总和,因为最每个点都加了两遍,所以下面输出时,先除2,再减ans;
        举个例子:在1 2 3 4 3 2 这个样例中,对于2加了一遍3与2的差,但对于又3加了一遍3与2的差 
        这就相当于加了2个3与2的差,其余都是这样,那最后计算出的ans是10而不是5,所以除2*/ 
    }
    cout<<sum/2-ans<<endl;
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

 

posted @ 2016-11-13 16:33  一蓑烟雨任生平  阅读(200)  评论(0编辑  收藏  举报