100. 增减序列(差分+贪心)

 假如给我们一个序列,并计算他们的差分数组

则假设p为差分数组(不包括b[1],从b[2]开始)中正值的总和        q为差分数组中负值的总和

P=4                                        q=0

先给出公式,最小操作数为min(p,q)+|p-q|,方案数为1+|p-q|

下面详细解释

        关于最小操作数

1.首先,有四个操作 

2.min(p,q)解析

3.|p-q|解析

关于方案数目

        方案数=1+|p-q|解析

 方案数的验证


1.首先,有四个操作 

这里j是作为右端点,改变的是元素[i,j-1]中所有的元素,在差分数组的角度是b[i]+-1,b[j]+-1;
情况1: 2<=i,j<=n,选b[i]和b[j]

这样会改变b[2],b[3]…b[n]中两个数的值,尽量多用(最好)  b[l]+=c  b[r+1]-=c

情况2:i=1,2<=j<=n 选b[j]和b[1],对某个前缀进行操作    这样子会改变b1,b1+-1

情况3: 2<=i<=n,j=n+1 对某个后缀进行操作                      这样子会改变b[n],bn+-1

情况4:i=1,j=n+1 对某个序列都进行操作(直接不用)

2.min(p,q)解析

根据贪心的思想,我们要让数组中所有元素相同的话,就是要让b[2~n]都变为0,这样子a[2~n]就都是a[1]了

假设p为差分数组(不包括b[1],从b[2]开始)中正值的总和        q为差分数组中负值的总和

在b数组中选择两个数,2<=i<j<=n  其中b[i]<0(b[i]>0)  ,b[j]>0(b[j]<0),       b[i]b[j]异号

注意是选择2<=i<j<=n的两个点,也就是第一个操作(贪心的思想)

注意,这里先不选择b[1]和b[n+1],因为p,q没有记录他们的差分值

选择的意义在于

根据差分的定义,我们每一次修改区间[l,r],都是b[l]+=1,b[r+1]-=1(或者相反)

那么在差分数组选择的i,j-1就是左右端点,我们贪心的选择一正一负,正数不断--,负数不断++,直到有一个为0,就换一个端点,这样子就是最小操作数

3.|p-q|解析

这些还需要填(挖)的坑的次数恰好是|p-q|  p-q的绝对值

为什么是|p-q|,因为我们是选择2到n的一个点作为左端点++(--),另一个点作为右端点--(++)

由于p,q是记录差分数组[2~n]所有的值,我们每次操作都只会让一个正数--,一个负数++,他们进行的操作次数是一样的,进行min(p,q)操作后,在数组[2~n]中值不为0的总和一定为|p-q|或者-|p-q|

这时候剩下的点就在[2~n]中,我们选择一个和b[1]或者b[n+1]配对,也就是进行第二种或者第三种操作,由于剩下的值是|p-q|...那么就只要进行|p-q|次+1或者-1操作就行了

下面是一个案例

先进行第一种操作 

我们先选择 b[2]  和  b[4]  进行3次+操作,然后b[4]=0,就换成 b[2] 和 b[3]  进行一次+操作,这样子[2~4]的b数组值都是0,(注意这里进行的是区间修改,[L,R]的点都被++)

 发现还有一个b[5]<0,那么就选择他和6进行++操作

 这时候对于b[2]~b[n]的点都操作完毕了,但是还有1个点b[5]没有满足条件

就进行第二或第三种操作

选择1或n+1作为一个端点,没有满足条件的作为另一个端点操作

 那就选择 b[5] 和 b[7] (n+1=6+1=7)  最后进行一次++操作  

方案数=1+|p-q|解析

方案数表示的是情况2,3的分配


 方案数的验证

数组和差分数组

 可能的方案

        1.  选了 3次  操作,把bn 减1减了3次

        2.选了 2次  操作,1次 2 操作

        2.选了 1次  操作,2次 2 操作

        2.选了 0次  操作,3次 2 操作

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=100005;
int a[N],b[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {   
        cin>>a[i];
        b[i]=a[i]-a[i-1];
    }
    long long q=0,p=0;
    for(int i=2;i<=n;i++)
    {
        if(b[i]<0) p-=b[i];
        else q+=b[i];
    }
//pq可能很大需要开longlong ,min(p,q)+|p-q|可以转为max(p,q)
    cout<<max(p,q)<<endl;
    cout<<1+(int)fabs(p-q);
//注意fabs返回值是浮点值,需要转化为int
}

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值