笔记
【问题描述】
给定一个长度为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; }