AcWing100.增减序列 差分+贪心
题目描述
分析
题目要求通过对区间[l,r]的操作(+1或者-1)使数列中数都一样.
我们发现满足上述要求的数列的差分数组的a[2…n]一定是[0,0,0,…]. a[1]是val
据此, 我们可以将问题转化成经过最少次对区间[l,r]的操作使差分数组a[]变为[val,0,0,0,…]
下面是贪心的部分:对于这个差分数组,我们可以对区间[l,r]有四种操作:(每一种操作都是+1,或者-1, 只不过影响的区间端点不一样)
对原数列的区间[l,r]操作(如+1), 其差分数组会受影响: a[l] += 1, a[r+1] -= 1
① 2 ≤ l ≤ n, 2 ≤ r ≤ n: 可以改变a[2…n]中的两个数(最优)
② l = 1, 2 ≤ r ≤ n 可以改变a[1]和a[2…n]中的一个数
③ 2 ≤ l ≤ n, r = n 可以改变a[2…n]中的一个数和a[n+1],但是我们的目标是吧a[2…n]全变为0
④ l = 1, r = n 可以改变a[0]和a[n+1] (没有作用)
综上, 最少的操作次数 = 选择操作① + 选择操作②(或③) 即 min(pos, neg) + abs(pos-neg)
而能得到多少种结果, 就取决是a[1]的值val, 而val取决操作②所产生的值 即 abs(pos-neg)+1
感觉自己写的不太清晰, 不过还是记录一下思路
更优的体验:Y总的视频讲解 大佬的博客讲解
实现
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int N = 1e5 + 9;
int n;
int a[N]; // 差分数组
long long pos, neg; // 差分数组a[2..n]中正数的和, 负数的和 开int会爆
int main()
{
cin >> n;
for(int i=0; i<n; i++) cin >> a[i];
// 处理出差分数组a[]
for(int i=n-1; i>=1; i--)
a[i] -= a[i-1];
for(int i=1; i<n; i++)
a[i] > 0 ? pos+= a[i] : neg += abs(a[i]);
// 核心代码: 贪心
// 最少操作次数: 进行第一种操作 + 进行第二(或第三)种操作
cout << min(pos,neg) + abs(pos - neg) << endl;
// 得到的结果数取决于差分数组中a[1]的值: 即进行第二种操作产生的结果数
cout << abs(pos - neg) + 1 << endl;
return 0;
}