1. 程式人生 > 實用技巧 >[淺講]二維字首和

[淺講]二維字首和

字首和,為什麼要用字首和,目的是更方便快捷的使用。我們定義字首和陣列為\(sum\),當然一維的字首和很簡單\(sum_i\)表示在第i個位置(包括的i個位置)前的所有權值的和,即\(\sum_{j=1}^{i}a_j\)

\[\begin{equation} \begin{aligned} sum_i=sum_{i-1}+f_i \end{aligned} \end{equation} \]

那麼二維字首和怎麼計算呢,我們畫個圖;

我們定義\(sum_{i,j}\)管轄的區域是①+②+③+④,\(sum_{i-1,j}\)是①+③,\(sum_{i,j-1}\)是①+②,\(sum_{i-1,j-1}\)

是①。那麼我們思考一下,\(sum_{i,j}\)怎麼由\(sum_{i-1,j}\)\(sum_{i,j-1}\)\(sum_{i-1,j-1}\)以及這個點的權值(圖中的④也就是\(f_{i,j}\))轉換過來呢。我們列出式子:

\[\begin{equation} \begin{aligned} sum_{i,j}=sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}+f_{i,j} \end{aligned} \end{equation} \]

為什麼?我們看圖,因為\(sum_{i-1,j}\)是①+③,\(sum_{i,j-1}\)是①+②,兩個區域相加,是不是重複加了①這個區域所以減掉①,也就是減掉\(sum_{i-1,j-1}\)

。沒問題吧,然後我們現在擁有了①,②,③三個區域還差一個④,最後加上\(f_{i,j}\)就行了,這樣我們就做到了初始化二維字首和陣列。
那麼我們怎麼計算呢?

假如我們要求④這個區域的值,也就是像之前一樣推演一下就好了

\[\begin{equation} \begin{aligned} sum_{x2,y2}-sum_{x1-1,y2}+sum_{x2,y1-1}+sum_{x1-1,y1-1} \end{aligned} \end{equation} \]

給道例題

題目描述

作為在虛擬世界裡統帥千軍萬馬的領袖,小\(Z\) 認為天時、地利、人和三者是缺一不可的,所以,謹慎地選擇首都的位置對於小 Z 來說是非常重要的。

首都被認為是一個佔地 \(C\times C\) 的正方形。小 Z 希望你尋找到一個合適的位置,使得首都所佔領的位置的土地價值和最高。

輸入格式

第一行三個整數 \(N,M,C\),表示地圖的寬和長以及首都的邊長。

接下來 \(N\) 行每行 \(M\) 個整數,表示了地圖上每個地塊的價值。價值可能為負數。

輸出格式

一行兩個整數 \(X,Y\),表示首都左上角的座標。

輸入 #1

3 4 2
1 2 3 1
-1 9 0 2
2 0 1 1

輸出 #1

1 2

說明/提示

對於 \(\%60\) 的資料,\(N,M\le 50\)

對於 \(\%90\) 的資料,\(N,M\le 300\)

對於 \(\%100\) 的資料,\(1\le N,M\le 10^3,1\le C\le \min(N,M)\)

一道模板題吧,就注意下輸出的是左上角的座標,畫下圖就行了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
int n,m,c;
int sum[maxn][maxn];
int ansx,ansy,maxx=-0x7fffffff;
int main(){
	cin>>n>>m>>c;
	for(int i=1;i<=n;i++)
	 for(int j=1,x;j<=m;j++){
	 	cin>>x;
	 	sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+x;
	 }
	for(int i=c;i<=n;i++)
	 for(int j=c;j<=m;j++){
	 	if(sum[i][j]+sum[i-c][j-c]-sum[i-c][j]-sum[i][j-c]>maxx){
	 		maxx=sum[i][j]+sum[i-c][j-c]-sum[i-c][j]-sum[i][j-c];
	 		ansx=i,ansy=j;
		 }
	 }
	cout<<ansx-c+1<<" "<<ansy-c+1<<endl;
	return 0;
}