1 つ目は、接頭辞と
1、接頭語と思考
プレフィックス合計は単純なアイデアです。まず、1 次元のプレフィックス合計を例に挙げます。これは、区間をすばやく解決するために使用されます。区間を解決する必要がある場合、複数回トラバースする可能性が高いため、 time 計算量はO(n)に達しますが、このアルゴリズムをM回クエリする必要がある場合、時間計算量は加速します。このため、この時間計算量を軽減する簡単な方法はありますか?
区間[n, m]を解くとき、単純なアルゴリズムでa m +a m-1 +...+a nのアイデアを使用して、この区間に対する単純な解を完成させることができます。このプロセスでは、配列を読み取るときに、最初の n個の合計を見つける配列を作成することもできます。この配列ツールとS [m] - S [n]のアイデアを使用して、この区間の解。
次に、2 次元のプレフィックスの合計を見てみましょう。2 次元のプレフィックスの合計は、1 次元のプレフィックスの合計よりも複雑です。2 次元の配列を行列として考えることもできます。これについては、プレフィックスの合計行列を解くことができます。このプレフィックスの合計を解く方法はアイデアです。次の図で、 A1領域のプレフィックスの合計を解きたいことがわかります。まず、 A2 + A3のアイデアを使用する必要があります。 -A4 +D1で解決します D1 はこの点の値です。このようにして 2 次元配列の構築プロセスが完了しますが、
配列の構築プロセスが完了した後、特定の領域の値をどのように解くことができるでしょうか。
このシミュレーション操作を使用すると、2 次元のプレフィックス和の処理を完了できます。2 次元のプレフィックス和を使用する場合、式 A1 + A2 -A3 -A4 に従うだけで対数計算操作を完了できます。
2. プレフィックスサムのコード実装
1 次元のプレフィックス和の実現: 上記のアルゴリズムの考え方に従って単純に記述し、出力するときに注意してください。この出力の演算は **S[x] - S[y - 1] ** 演算です。
#include <iostream>
using namespace std ;
const int N = 100010 ;
int q[N] , s[N] ;
//存储该点的数值的q[N]数组,存储前缀和的s[N]数组
int main ()
{
int n , m ;
cin >> n >> m ;
for(int i = 1 ; i <= n ; i ++ )
{
cin >> q[i] ;
s[i] = s[i - 1] + q[i] ;
}
//完成两个数组的建立
while ( m -- )
{
int x , y ;
cin >> x >> y ;
cout << s[y] - s[x - 1] << endl;
}
return 0 ;
}
2 次元のプレフィックスの合計: 配列の範囲外の問題の発生を防ぐために注意する必要があるのは、一般に、添字 0 ではなく添字 1 からストレージ操作を開始することです。
#include <iostream>
using namespace std ;
const int N = 1010 ;
int q[N][N] , s[N][N] ;
int main ()
{
ios::sync_with_stdio(0);
int n , m , cnt ;
cin >> n >> m >> cnt ;
for(int i = 1 ; i <= n ; i ++ )
for(int j = 1 ;j <=m ;j ++ )
cin >> q[i][j] ;
//完成q数组的构建
for(int i = 1 ; i <= n ; i ++ )
for(int j = 1 ;j <=m ;j ++ )
s[i][j] = s[i - 1][j] + s[i][j -1] + q[i][j] - s[i - 1][j - 1] ;
//完成s数组的构建
while (cnt -- )
{
int x1, x2, y1 , y2 ;
cin >> x1 >> y1 >> x2 >> y2 ;
cout << s[x1 - 1][y1 - 1] + s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] << endl ;
}
return 0 ;
}
第二に、差分アルゴリズム
1、差分アルゴリズムの考え方
差分アルゴリズムは間隔演算を解決し、プレフィックス合計は間隔合計を解決します。いわゆる差分は、関連する演算を実行するために、この桁と前の桁の差を格納する関連配列を開くことです。 。1 つ目は 1 次元のプレフィックスの合計です。配列の格納操作を完了するにはq[n] - q[n -1]のみを使用します。間隔操作を実行してx を[n,m]間隔に追加すると、考え方は、c[n] += x 、 c[m + 1] -= xということです。この配列を出力する場合、ステップバイステップで追加することで出力できます。
2次元差分配列は比較的複雑です 2次元差分は2次元の接頭語和のように考えることはできません この2つは考え方が異なる2つのものと言えます 2次元差分配列は逆方向の演算ですまず 2 つのフェーズ 加算する配列はa[x1][y1]と **a[x2 + 1][y2 + 1]** で、減算の演算は最大値に加算する演算です。次の配列は基本的に次のようにみなされることがわかります。
これは差分演算を完全に解釈し、差分行列の読み込み操作を説明しますが、出力操作は完了しておらず、その出力操作は接頭辞と同様の操作である必要があります。両辺で合計し、対角で減算します。
2. 差分アルゴリズムの実現
1次元差分アルゴリズム
#include <iostream>
using namespace std ;
const int N =100010 ;
int q[N] , k[N] ;
int main ()
{
int n , m ;
cin >> n >> m;
for(int i = 1 ; i <= n ;i ++ )
{
cin >> q[i] ;
k[i] = q[i] - q[i - 1] ;
}
//完成原数组和差分数组的赋值操作
while ( m -- )
{
int x ,y , z ;
cin >> x >> y >> z;
//核心步骤
k[x] +=z ;
k[y + 1] -= z ;
//以上两步
}
int cnt = 0 ;
for(int i = 1 ; i <= n ; i ++ )
{
cnt +=k[i];
cout << cnt << " ";
}
return 0 ;
}
2D 差分アルゴリズム
#include <iostream>
using namespace std ;
const int N = 1010 ;
int q[N][N] , k[N][N] ;
void insert (int x1 ,int y1 ,int x2 ,int y2 ,int z)
{
k[x1][y1] += z;
k[x2 + 1][y2 + 1] += z ;
k[x2 + 1][y1] -= z ;
k[x1][y2 + 1] -= z ;
}
int main ()
{
int n , m , p ;
cin >> n >> m >> p;
for(int i = 1 ; i <= n ; i ++ )
for(int j = 1 ; j <= m ; j ++ )
{
cin >> q[i][j] ;
insert(i , j , i , j,q[i][j]);
}
while (p --)
{
int x1 ,y1, x2 ,y2 ,cnt ;
cin >> x1 >> y1 >> x2 >> y2 >> cnt ;
insert(x1 ,y1 ,x2 ,y2 ,cnt );
}
for(int i = 1 ;i <= n ;i ++ )
{
for(int j = 1 ;j <= m ;j ++ )
{
k[i][j] += k[i - 1][j] + k[i][j - 1]- k[i -1 ][j - 1];
cout << k[i][j] << " ";
}
cout << endl;
}
return 0 ;
}
3、2つのアルゴリズムの考え方
差分アルゴリズムでもプレフィックス和アルゴリズムでも、それらはすべて、配列ツールを使用してこのアルゴリズムの操作を完了することに焦点を当てています。これらの配列の作成、間隔の使用、および出力操作をマスターするだけで、これらのアルゴリズムを理解すると同時に、プレフィックスの合計と差の配列は逆であるため、プレフィックスの合計の作成は実際には差の出力と同等になります (高度なバージョン、差の配列に値を割り当てる)。差分の作成もプレフィックス合計の出力と似ています(まったく同じではありません)。