mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1000 文字
3 分
アルゴリズム学習:前計算和・差分・双方向ポインタ
2022-07-16

1. 累積和#

1.1 アルゴリズムの原理#

いわゆる累積和とは、前方のすべてのデータの和を記録しておくことを指し、所要の中間データを得る際には、o(1)の時間計算量でデータを求めることができる。

  • 一次元配列の累積和

1からiまでのすべての項の和を求める。

  1. 当運算到第i位時、前i-1位はすでに運算完了しているため、a[i] = a[i] + a[i-1]

  2. 当需要[l,r]の和時は、a[r]-a[l-1]で求められる

  • 二次元配列の累積和

左上端点、右下端点がそれぞれ(1,1)、(i,j)の長方形内のデータの和を求める

i,jへ到達した時、前i-1排目と、第i排目の前j-1位はすべて計算済みであるため、a[i][j] = a[i][j]+a[i-1][j]+a[i-1]-a[i-1][j-1]

  1. 左上端点、右下端点がそれぞれ(x1,y1)(x2,y2)の長方形内のデータの和は、a[x2][y2]-a[x2][y1-1]-a[x1-1][y2]+a[x1-1][y1-1]

1.2 例題#

参考コード:

void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],a[i]+=a[i-1];
while(m--){
cin>>l>>r;
cout<<a[r]-a[l-1]<<endl;
}
}

参考コード:

void solve(){
cin>>n>>m>>q;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
cin>>a[i][j],a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
while(q--){
cin>>x1>>y1>>x2>>y2;
cout<<a[x2][y2]-a[x2][y1-1]-a[x1-1][y2]+a[x1-1][y1-1]<<endl;
}
}

2. 差分#

2.1 アルゴリズムの原理#

差分とは、各データと前のデータとの差を記録することで、区間に対して操作を行う際にo(1)の時間計算量で処理を完了できる。

また、差分配列の前縁の和は元の配列そのものになることが分かる。

  • 一次元配列の差分

前項との差を求める。 [l,r] 範囲の数を加減する必要がある場合、b[l]+=cb[r+1]-=c で実現

  • 二次元配列の差分

二次元差分配列の構築は、その前縁和の結果から逆算して考えることができる。

(x1,y1)(x2,y2) の長方形内のデータをすべて c 加えるとき、すなわち a[x1,y1]~a[x2,y2] がすべて c となるようにします。したがって b[x1,y1]+=cb[x1,y2+1]-=cb[x2+1,y1]-=cb[x2+1,y2+1]+=c とすれば、区間内のデータのみを加えることができます。

20201217171134926.png

差分配列を構築する際、それは長さ1の長方形に対して a[i,j] を加えることになるため、2次元差分配列を実現できる。

2.2 例题#

参考コード:

void solve(){
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) b[i] = a[i]-a[i-1];
while(m--){
cin>>l>>r>>c;
b[l]+=c;b[r+1]-=c;
}for(int i=1,t=0;i<=n;i++) t+=b[i],cout<<t<<' ';
}

参考コード:

void add(){
b[x1][y1] += c;b[x1][y2+1] -=c;
b[x2+1][y1] -= c;b[x2+1][y2+1] +=c;
}
void solve(){
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
cin>>a[i][j],x1=x2=i,y1=y2=j,c=a[i][j],add();
while(q--){
cin>>x1>>y1>>x2>>y2>>c;
add();
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j] = b[i][j] + a[i-1][j]+a[i][j-1]-a[i-1][j-1];
cout<<a[i][j]<<' ';
}cout<<endl;
}
}

3. 双指针#

3.1 アルゴリズムの原理#

ダブルポインタ問題は、列の性質に基づき、有効なクエリ範囲を指針 j で記録することにより、計算時間の複雑さを低減します。

ダブルポインタ問題は主に以下の2つに分類されます。

  1. 一つの列で、二つのポインタが区間を維持する。
  2. 二つの列で、ある種の順序を維持する

基本テンプレート(AcWing より)

void solve(){
for(int i=0,j=0;i<n;i++){
while(j<i && check(i,j)) j++;
//具体的な問題ロジック
}
}

3.2 例题#

解法の逻辑:cnt 配列を用いて [j,i] 区間内のデータの出現回数を記録します。ia[i] をすでに出現させている場合、[j,i-1] 内で a[i] の出現回数が0になるまで j を前方へ動かします。長さを途中で記録します。

void solve(){
for(int i=1,j=1;i<=n;i++){
if(cnt[a[i]]){
while(cnt[a[i]]) cnt[a[j++]]--;
}
cnt[a[i]]++;
ans = max(i-j+1,ans);
}cout<<ans<<endl;
}

解法の逻辑:数列は昇順なので、a[i]+a[j] > x となると、a[i+1]+a[j] は必ず x より大きくなる。従って判定が必要な境界を j で記録することで探索時間を削減できる。

void solve(){
for(int i=0,j=m-1;i<n,j>0;i++){
while(a[i]+b[j]>x) j--;
if(a[i]+b[j]==x){
cout<<i<<' '<<j<<endl;
return 0;
}
}
}

解法の逻辑:母串中に子文字列は順序通り現れるため、母串中の各位置に対応する位置を順に探すことができます。

void solve(){
for(int i=0,j=0;i<n && j<m;i++,j++){
while(a[i] != b[j] && j<m) j++;
if(i==n-1 && j < m) {cout<<"Yes"<<endl;return 0;}
}cout<<"No"<<endl;
}
共有

この記事が役に立ったときは、ぜひ他の人に共有してください!

アルゴリズム学習:前計算和・差分・双方向ポインタ
https://dreaife.tokyo/jp/posts/prefix-sum-difference/
著者
dreaife
公開日
2022-07-16
ライセンス
CC BY-NC-SA 4.0

一部の情報は古い可能性があります

関連した投稿 スマート
1
アルゴリズム学習:ビット演算・離散化・区間マージ
algorithm ビット演算、離散化、区間マージのアルゴリズムを紹介します。ビット演算は2進数処理に用いられ、離散化は疎データの保存・検索を最適化し、区間マージはソートと重なり判定によって複数区間を効率的に統合します。理解と応用を助けるため、関連する例題と参考コードも掲載しています。
2
高精度演算の学習記録
algorithm 高精度演算には加算、減算、乗算、除算が含まれ、通常のデータ型の範囲を超える大きな数を扱う際に用いられます。加算と減算は筆算のシミュレーションで実装し、乗算と除算は各桁を順に処理しつつ先頭のゼロ除去に注意します。理解と実装に役立つ例題と参考コードも掲載しています。
3
ソートと二分探索の学習
algorithm クイックソートとマージソートの原理と実装を、時間計算量や関連問題とあわせて紹介します。クイックソートは中間値を基準に数列を2つに分割して整列し、マージソートは整列済み部分列を統合していきます。整数・浮動小数点数に対する二分探索アルゴリズムとその実装方法についても解説します。
4
面接アルゴリズム学習1
algorithm 蛇行行列の埋め込み、単方向連結リストの高速ソート、ピーク値や極小値の探索、卵の硬度問題、最小値取得をサポートするスタック、連結リストの循環開始ノード探索など、複数のアルゴリズム面接問題とその解法をまとめています。各問題には詳細な説明、入出力形式、サンプルコードが付いています。
5
新時代における第一回の選抜
life AI技術の発展に伴い、高度なモデルを利用するコストが社会の階層化を招き、経済力のある人だけがこれらのモデルを使えるようになる可能性がある。現在の価格はまだ許容範囲だが、将来的な値上がりにより大多数が負担できなくなり、第一のふるい分けが生じるかもしれない。筆者はこの現象に不安を覚えつつ、AIの応用がすでにプログラミング領域を超え、より広範な産業へと広がっていることも実感している。新しい世界の課題と機会に向き合いながら、個人は時代の推進力に押されつつ探索を続けている。

目次