首页 > 编程知识 正文

求数组最小差值最优算法,差分数据是什么意思啊

时间:2023-05-03 12:14:01 阅读:147129 作者:2810

显然什么是差分配列就是个数组

本菜鸡学习很浅,先说说我自己知道的差距分配排吧!

首先,让我解释一下差别是什么:

差分其实是数据之间的差,是什么样的数据差呢? 是上面所给的原始数组的相邻元素之间的差值。 在 d[i]=a[i+1]-a[i]上,只需重复一次for循环即可求出差分配串。

板栗,差分配列,先

要说如何求出差分配序列,其实差分配序列是辅助数组,从侧面表示了某一序列的变化,一般用于对序列进行区间修改的操作

果然上面表里不一的栗子,需要进行以下操作。

1、区间【1,4】的数值全部加3

2、区间【3,5】的数值全部减5

很简单吧。 也可以列举。 但是,如果给你的数据量为1e5,操作量为1e5,时间限制为1000ms,你能暴力列举过去吗?T直到你怀疑人生的直接。 在这种情况下,必须使用差分配列。

其实是动听的钢笔将原始数组中元素同时加上或者减掉某个数,那么他们的差分数组其实是不会变化的。

试着利用这个思想缩小区间吧。 是缩小了的例子的区间【1,4】吧。 可见,只有d[1]和d[5]发生了变化,d[2]、d[3]和d[4]保持不变。

执行以下操作:

这时我们就会发现这样一个规律,当对一个区间进行增减某个值的时候,他的差分数组对应的区间左端点的值会同步变化,而他的右端点的后一个值则会相反地变化,其实这个很好理解

其实这么多代码就可以了

wile(m---- )//操作次数cinleftrightchange; //左右端点及其变化的值d[left]=change; d[right 1]-=change; }既然修正了差分配列能做什么的区间,那么差分配列的作用一定是求出进行了多次区间修正的数组

注意仅在区间元素同时增加或减少相同数量时可用

因为我们的差分配列是从原排列的相邻的两个项中取差求出的。 即,d[i]=a[i]-a[i-1]; 那么,反过来,试着求出修正后的a[i]吧?

直接反过来即得 a[i]=a[i-1]+d[i]

事实证明这是正确的,具体的证据法就不普及了,有空再补吧;

更新数组a的方法如下所示。 这样,就可以求出更新后的数组a。 不是比分段树快多了吗?

for(intI=1; i=n; I ) a(I )=a(I-1 ) b ) ); 差分配列是如何翻转使用的,还是该语句,区间修正当然也有与树数组组合使用的。 直接看主题吧

HDU-1556 Color the Ballhttp://acm.hdu.edu.cn/showproblem.php?pid=1556这个问题的结果已经不能结果了吧。 向上看,闭着眼睛也能敲

直接附加代码吧

# include iostream # include cstdio # include algorithm # include cstring # include string # include cmath # definelllllonglong # defing 常数int mm=1e510; int a[mm],b[mm]; int x,y; int main () { int n; while(scanf('%d ',n ) n ) { mem(a ) ) a,0 ); mem(b,0; for(intI=1; i=n; I )扫描(' % d % d )、x、y ); b[x]; b[y 1]--; }for(intI=1; i=n; I ) a(I )=a(I-1 ) b ) ); for(intI=1; in; I ) printf('%d ',a[i]; printf(%d(n ),a[n]; }返回0; }让我们来看看更高级的主题吧

POJ -3263 Tallest Cowhttp://poj.org/problem?

id=3263

这道题他要你求出每头牛的最大可能的高度,我们的思路就是先让所有的牛都和最高的一样高,关于他给到的牛能看到的区间进行修改,让中间的数至少减一,注意这里修改的一定是开区间,区间端点不能修改。再一个就是关于去重问题。。。谁会想到他还有这么一个坑呢????

只要看透了是个差分也很简单,代码附上

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<string>#include<cmath>#define ll long long#define mem(a,b) memset(a,b,sizeof(a))using namespace std;const int inf=0x3f3f3f3f;const int mm=1e4+10;int a[mm],b[mm];int vis[mm][mm];int main(){int n,pos,h,r;scanf("%d%d%d%d",&n,&pos,&h,&r);for(int i=0;i<=h+1;i++)a[i]=h; int x,y; for(int i=1;i<=r;i++){scanf("%d%d",&x,&y);if(x>y) swap(x,y);// if(vis[x][y])//判重 continue;vis[x][y]=1;b[x+1]--;//后面的减少 b[y]++;//前面的增加 }for(int i=1;i<=n;i++){a[i]=a[i-1]+b[i];printf("%dn",a[i]);} return 0;}

今天测试碰到了一个二阶的差分题,我居然不会,用一阶差分直接被T了就很难受,听了旁边的大佬才知道,可以用两个差分来做,膜拜一下Orz

CodeForces - 296C  Greg and Array?  https://cn.vjudge.net/problem/CodeForces-296C

首先说一下,这道题的题意吧,反正我一开始模拟了半年也没看懂,太难受了。

这道题意的坑在于那k个询问:

其实他给的k个询问并不是直接让你修改的区间,而是一个问题编号的区间,他给的[L,R]是告诉你要执行编号为 L~R 的操作

这就告诉我们要把问题存起来先,也算是一个离线操作吧。

这道题显然可以使用线段树,但是我觉得要打好多行代码,就选择了刚学的差分,我是用一阶差分做的,直接两层大循环,然后T死,这里有个代码,来纪念一下我自己被T惨的下场,不具备参考价值 嘻嘻嘻。https://paste.ubuntu.com/p/zzSr63cM5J/

这时我们就需要 用两个差分,第一个差分来维护要进行的操作的区间端点,就是那K个询问,这是有点难受的,反正我一上来没有想到,是旁边的大佬偷偷告诉我的,第二个差分就是来维护我们的原始数组,进行修改,修改的方法可以往上看↑↑↑↑↑↑

下面是可以参考的代码了:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<string>#include<cmath>#define ll long long#define mem(a,b) memset(a,b,sizeof(a))using namespace std;const int inf=0x3f3f3f3f;const ll mm=1e5+10;ll a[mm],d[mm];ll op[mm][5];ll n,m,k;ll cnt[mm];ll num[mm];int main(){cin>>n>>m>>k;for(ll i=1;i<=n;i++)cin>>a[i];for(ll i=1;i<=n+1;i++)d[i]=a[i]-a[i-1];for(ll i=1;i<=m;i++)cin>>op[i][1]>>op[i][2]>>op[i][3];ll x,y;while(k--){//lixiancin>>x>>y;cnt[x]++;cnt[y+1]--;}for(int i=1;i<=m;i++)num[i]=num[i-1]+cnt[i];for(ll i=1;i<=m;i++){d[op[i][1]]+=op[i][3]*num[i];d[op[i][2]+1]-=op[i][3]*num[i];}for(ll i=1;i<=n;i++)a[i]=a[i-1]+d[i];for(ll i=1;i<=n;i++)cout<<a[i]<<' ';//cout<<a[n]<<endl;return 0;}

还有一道高阶题目:

POJ 3468-A Simple Problem with Integers   http://poj.org/problem?id=3468

这个题也果的一批,让你进行区间修改与区间求和查询,刚学线段树的时候用线段树写的,那是一个受罪啊,也就写了两千多个字符吧,是真的累。。。下面是一片关于这道题线段树解法的blog

https://blog.csdn.net/qq_44786250/article/details/98474701

这里我们讲一个高深一点的做法,那就是差分。但是有一点,差分对区间修改好用,但是区间求和还是需要暴力啊,依旧让你TTT这就是这道题的高深之处了,那就是结合树状数组,差分负责区间修改,树状数组进行区间求和,下面的代码也就888个字符,不过有点费脑,或许用的多了就好了吧!

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<string>#include<cmath>#define ll long long#define mem(a,b) memset(a,b,sizeof(a))using namespace std;const int inf=0x3f3f3f3f;const ll mm=1e5+10;ll n,m;ll a[mm],b[mm],c[mm];ll sum[mm];ll lbt(int x){return x&-x;}void update(int pos,int k){for(int i=pos;i<=n;i+=lbt(i)){b[i]+=k;c[i]+=k*(pos-1);}}ll getsum(int pos){ll res=0;for(int i=pos;i>0;i-=lbt(i)){res+=pos*b[i]-c[i];}return res;}int main(){scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}char op[2];ll x,y,z;while(m--){scanf("%s",op);if(op[0]=='Q'){scanf("%lld%lld",&x,&y);printf("%lldn",sum[y]-sum[x-1]+getsum(y)-getsum(x-1));}else {scanf("%lld%lld%lld",&x,&y,&z);update(x,z);update(y+1,-z);}}return 0;}

 

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。