1. 程式人生 > >一維陣列和二維陣列字首和

一維陣列和二維陣列字首和

原創地址如下

http://blog.csdn.net/XT_NOI/article/details/72666275      (一維陣列)

http://m.blog.csdn.net/XT_NOI/article/details/72715904    (二維陣列)

一維字首和維護是一種基礎的小演算法,該演算法用我們所熟知的數列求和方式優化我們的某些查詢操作,是一種動態規劃的思想。

這裡為介紹一維字首和維護問題,我們來思考這個最簡單的問題:

問題描述:
已知n個數的數列a,有m次詢問,每次詢問給定l,r兩個數,求 alar 內所有數的和。注意l到r的區間包含 ala

r 兩個數。
輸入資料:
第一行n和m,接下來一行有n個數,表示數列a,接下來有m行,每行有兩個數l,r,詳細解釋參考問題描述。
輸出資料:
m行,每行一個求和解。
輸入樣例:
5 2
1 2 3 4 5
2 4
3 5
輸出樣例:
9
12
資料範圍:
0<N,M100000
ai 是int範圍內的任意整數

對於這個問題而言,我們一開始想到的思路是對於每一次查詢操作都遍歷一遍我們的陣列進行求和,這樣的操作每一次都會遍歷陣列,是O(nm)的做法,而對於題目的資料量,這樣一定會超時,於是我們要思考有沒有更加優化的解。

先考慮暴力的情況,我們對於每一次查詢操作,都遍歷一遍陣列,我們會發現,當我計算了某一區間值的時候,我計算其他區間,會重複計算某些值,我是否可以用一種方式,把這種重複利用起來。

於是我們想到了初中學過的前n項和問題。當我知道前r項和與前l-1項和,我是否就能求出從l到r的區間和?思考一下這個過程。前r項和裡面包含了前l-1項和,所以做一個減法就能求出l到r的區間和。那麼問題就到了我們如何求前n項和,這個問題更加簡單,我們只需要遍歷一次陣列就可以求到這個前n項和的陣列了,假設我們原陣列是a,前n項和的陣列是s,那麼我們只需要s1=a1之後維護動規方程si=si1+ai(i>1)即可。

那麼問題迎刃而解,我們分析一下新解法的複雜度,求出s陣列需要遍歷一遍原陣列,所以是O(n)的複雜度,對於每一次查詢操作我們只需要做一次減法,所以是O(1)的複雜度,一共有m次,所以是O(m)的查詢,最後的結果就是O(n+m)的時間複雜度。

以上就是關於一維的字首和維護問題和解法的介紹。

程式碼如下:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN=1000010;

int n,m,a[MAXN];

//讀入優化 
int read(){
    char ch=getchar();
    bool fl=0;
    int r=0;
    if(ch=='-'){
        fl=1;
        ch=getchar();
    } 
    while(ch>='0'&&ch<='9'){
        r*=10;
        r+=ch-'0';
        ch=getchar();
    }
    return fl?-r:r; 
}

//輸出優化 
void write(int x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9){
        write(x/10);
    }
    putchar(x%10+'0');
}

int main(){
    freopen("in.txt","r",stdin);
    freopen("std.txt","w",stdout);
    n=read();
    m=read();
    a[0]=read();
    for(int i=1;i<n;++i){
        a[i]=read();
        a[i]+=a[i-1];//前n項和求法,O(n)複雜度 
    }
    for(int i=0;i<m;++i){
        int l,r;
        l=read();
        r=read();
        if(l==1){
            write(a[r-1]);//輸出結果,單次O(1),總共O(m) 
        }else{
            write(a[r-1]-a[l-2]);//隱含的陣列下標越界問題,在我寫二維字首和的時候發現的 
        }
        putchar('\n');
    }
    return 0;
}

相信來看二維字首和維護的各位一定是對一維字首和維護問題有足夠的瞭解了,那麼二維的字首和維護實際上是在一維字首和維護的基礎上的升級,把一個數列升級成了矩陣,但是思想是一樣的,具體問題如下:

問題描述:
已知n*n的矩陣a,有m次詢問,每次詢問給定x1,y1,x2,y2四個數,求以(x1,y1)為左上角座標和(x2,y2)為右下角座標的子矩陣的所有元素和。注意仍然包含左上角和右下角的元素。
輸入資料:
第一行n和m,接下來一行有n行,每行n個數,表示矩陣a,接下來有m行,每行有四個數x1,y1,x2,y2,詳細解釋參考問題描述。
輸出資料:
m行,每行一個求和解。
輸入樣例:
5 2
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
2 2 4 4
3 3 5 5
輸出樣例:
117
171
資料範圍:
0<N10000<M100000
ai,j是int範圍內的任意整數

我們肯定能夠想到,如果單純的使用暴力解決這個問題是O(mn2)的做法,對於這個題的資料範圍是絕對超時的。所以我們也能想到是使用了一個類似於一維字首和維護的優化方法的優化方式。但那是什麼?當我們轉換成為矩陣的時候,前n項和的做法就不行了。

但我們還有一個東西叫做容斥定理。容斥定理是關於集合的一個定理。這個定理是小學奧數內容,而且我們在高中課程中學習集合的時候已經瞭解過了,但你或許不知道這個名字。

在小學奧數中,容斥定理被描述的極其簡單,如果我有n個抽屜,有m個蘋果,我往抽屜裡面放蘋果,保證每個抽屜都有蘋果的情況下,如果kn<m,那麼必定會有一個抽屜有至少k+1個蘋果。

當然,在我們學習過集合之後,容斥定理就可以這樣來描述:

設有A,B,C三個集合,這三個集合相互均有交集,並且三個集合之間也有交集。則這三個集合的並集可以表示為:

ABC=A+B+CABBCAC+ABC

我們使用文氏圖可以得到一個更直觀的解釋:

文氏圖

相信數學好的同學已經通過圖看出來這個公式了,用更通俗易懂的道理來解釋,三個集合,首先直接相加,發現ABBCAC的三個部分被重複添加了,之後減去這三個部分,又發現ABC的部分都被減去了三次,實際上對於這部分被加了三次(在A+B+C的時候這部分就被加了三次)之後又減了三次(在減去ABBCAC的時候分別被減去了一次),實際上就跟沒加過沒減過一樣,所以我們要再加一次。所以公式就變成上面那個樣子。

這裡我們只推了3個集合的容斥定理,對於多個集合思路是一樣的。我們可以把所有集合相加,同時減去所有偶數重複,加上所有奇數重複。簡單來記憶就是減偶加奇。這個過程可以自己推導,所以這裡不再贅述。

現在我們來把這個定理應用到我們的矩陣上。現在我們用S(x1,y1,x2,y2)表示以(x1,y1)為左上元素和以(x2,y2)為右下元素的矩陣中所有元素的和。對於任意S(x1,y1,x2,y2),它一定等於S(1,1,x2,y2)加上 S(1,1,x11,y11)減去 S(1,1,x11,y2)減去 S(1,1,x2,y11)。也就是這個公式:

S(x1,y1,

相關推薦

陣列陣列字首

原創地址如下 http://blog.csdn.net/XT_NOI/article/details/72666275      (一維陣列) http://m.blog.csdn.net/XT_NOI/article/details/72715904   

Java中陣列陣列儲存佔用記憶體大小問題

問題:在java中,一維陣列和二維陣列在資料量一樣的情況下,開闢的記憶體大小是怎樣的? 一、嘗試階段: 1、程式碼一: public class OneArrayMemory{ public static void main(String[] args){ int num1 = 1

使用反射建立陣列陣列

package com.iotek.classtype; import java.lang.reflect.Array; public class ReflectionArrayDemo { public static void main(String[] args) throws Except

axis2生成webservice服務端返回String[]String[][]陣列陣列解析

環境:用axis2生成服務端,用aixs做客戶端 1:直接返回String[];          public String[] testArr(String name) {     

Solidify實現一個智慧合約10(陣列陣列

固定長度的陣列 固定長度型別陣列的宣告及其通過length方法獲取陣列長度求和。 pragma solidity ^0.4.4; /* 陣列一旦建立,長度不可變 但裡面的內容可變 */ contract Sz { //定義長為5的陣列,並對其初始化。 uint[

Java中陣列陣列初始化

陣列屬於引用資料型別,在使用前必須初始化,否則會報NullPointerException(空指標異常:執行時異常) 一維陣列初始化: 動態初始化:(宣告並開闢陣列) 資料型別[ ] 陣列名稱=new 資料型別[長度] int[ ] data=new

JS中的陣列陣列

一維陣列: 對於一維陣列的宣告有以下幾種: 1.var fruit=new Array(); 2.var fruit =new Array(3); 3.var fruit = new Arrat('3', '4'); 二維陣列的宣告: 1.var array=new Array(new Arrat

指標指標指向陣列的一些問題

廢話少說,先上自己Dev c++上的程式碼: #include<stdio.h>int main (){int c[2][3]={15,2,3,4,5,6},*p,(*rp)[3],*q,i; p =(int*)c; rp=c; q=c;

Python實遍歷二維陣列題目:在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。

在準備國網的過程中,仍要刷題,程式設計題。痛並快樂著。喜歡Python語言,所以用Python進行敲磚頭。劍指offer題目一:在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列

指標陣列指標陣列

在函式傳參時候,陣列名作為引數,自動轉成指標,那麼二維陣列可不可以這樣做呢。void fun(int **a,int i,int j){int m = i*j;for (int i = 0; i < m; i++){cout << *(a[0] + i)<<endl;}}int

C/C++陣列陣列指標)——陣列的傳值陣列訪問

二維陣列既可以通過二維訪問也可以通過一維訪問。 對於二維陣列的傳值,引進了陣列指標。 #include <stdio.h> void foo(int *p,int n)//一維訪問 {

陣列陣列的定義及用法

一維陣列的定義型別說明符  陣列名 [常量表達式];在此說明:常量表達式必須是常量和常量符號。常量表達式定義了陣列的長度。在巨集定義中#define定義一個符號常量,則這個符號常量可以用來宣告陣列的個數#define n=9.....int arr[n];一維陣列的引用陣列必

java陣列陣列的定義及其初始化

public class ArrayTest2 { public static void main(String[] args) { Person[] p = new Person[3]; //未生成物件時,引用型別均為空 System.o

Java楊輝三角(陣列陣列方法)

範例:一維陣列 import java.util.Scanner; public class Demo { public static void main(String[] args) { S

陣列陣列

一堆陣列 陣列 int 的預設值是0 Sting的預設值是null boolean的預設型別是false 陣列一擔初始化他的長度是不可改變的 陣列的四步走: 1.宣告陣列 2.分配陣列 3.賦值 4.處理資料 陣列的格式int[ ] scores = new int[2];

JAVA 陣列 陣列陣列

一維陣列知識點 // 定義一個a陣列int[] a = new int[]{3, 4 , 5, 6};// 定義一個a2陣列int[] a2 = new int[]{3, 4 , 5, 6};//

陣列陣列的建立、初始化、使用、儲存、指標訪問

1.解析一維陣列的建立和初始化 2.一維陣列的使用 3.一維陣列的儲存 4.一維陣列的指標訪問 5.解析二維陣列的建立和初始化 6.二維陣列的使用 7.二維陣列的儲存 8.二維陣列的指標訪問 1.解析一維陣列的建立和初始化

陣列 陣列

    所謂陣列,是無序的元素序列。 若將有限個型別相同的變數的集合命名,那麼這個名稱為陣列名。組成陣列的各個變數稱為陣列的分量,也稱為陣列的元素,有時也稱為下標變數。用於區分陣列的各個元素的數字編號稱為下標。陣列是在程式設計中,為了處理方便, 把具有相同型別的若干元素按無序

徹底搞清C/C++中陣列陣列,指標,陣列指標指標陣列以及指向指標的指標,行地址列地址之間的關係

#include <iostream> using namespace std; void test(char **ptr) { for(;(strcmp(*ptr,"NULL"))!=0;ptr=ptr+1)        cout << *(ptr) <&l

C語言 函式返回陣列陣列

方法一: 萬能的結構體:構造陣列的結構體,將函式型別定義為此型別 但是考試的時候應該不太方便寫結構體,寫不下也會很麻煩,故介紹方法二 方法二: 指標傳遞: 1、返回一維陣列 例子:將陣列每一位加一: #include<stdio.h> #define N 10 int