列舉的一些分析

2022-06-13 12:54:12 字數 2912 閱讀 2752

遞增三元組——用統計量(字首和)化簡列舉

題目要求很簡單,給出三個陣列,找出滿足題意的所有組合的個數。

暴力列舉

剛開始沒在意,直接暴力列舉

#include#includeusing namespace std;

const int n = 100010;

int a[n], b[n], c[n];

int main()

} cout << ans;

return 0;

}

但是,碰到10000的資料就超時了,分析一下時間複雜度:

三個sort函式 : 3 * n *log(n)

三層輸入迴圈:3 * n

三層計算:最壞的情況是n ^ 3

大大的超過了1e9的時間複雜度,顯然是不行的。

初步引入統計量

瞄了一眼網上的題解,說是可以用統計量的方式進行統計排序

初步想法是:設a[i]就是陣列a中i出現的次數,a[n]記錄一共出現了幾個不同的數

**:

#include#includeusing namespace std;

const int n = 100010;

int a[3][n];

int main()

int ans = 0;

for(int i = 0; i <= a[0][n - 1]; i ++)

if(a[0][i])

for(int j = i + 1; j <= a[1][n - 1]; j ++)

if(a[1][j])

for(int k = j + 1; k <= a[2][n - 1]; k ++)

if(a[2][k])ans += a[0][i] * a[1][j] * a[2][k];

cout << ans;

return 0;

}

但是,還是tle,

時間複雜度分析:

在計算過程中的時間複雜度:n * n * n,在一定程度上反而增大了,因為有的數可能沒出現過,但是還是要判斷一下

進一步分析

轉化一下思維,先分析資料,由於是100000的資料,n*n的時間複雜度就會超時,所以需要把迴圈限制在一層,把時間複雜度限制在nlgn以內。

題目要求的是ai < bj < ck,可以看到b陣列處在核心位置,所以需要列舉一次b,然後只需要將滿足條件的a,c的個數相乘就能夠完成。

這就轉化到字首和問題,建立乙個陣列,ac[i],代表陣列a中處於區間[0 , i]的數字出現的個數,那麼顯然c[i]就得是字尾和,代表處於區間[i, n]的數字的個數

#include#includeusing namespace std;

const int n = 100010;

int a[n], b[n], c[n];

int ac[n], cc[n];

int main()

//這裡的ac[i]代表陣列a中i的個數

for(int i = 1; i < n; i ++)

ac[i] += ac[i - 1];//這裡才是字首和

for(int i = n; i > 2; i --)

cc[i - 1] += cc[i];//字尾和

long long ans = 0;

for(int i = 0; i < n; i ++)

if(b[i])ans += (long long)ac[b[i] - 1] * cc[b[i] + 1];//注意資料有可能非常大,注意型別轉換

cout << ans;

return 0;

}

分析一下時間複雜度

輸入:3*n

計算a,c陣列中每個數的個數:n

計算字首、字尾和:2 * n

結果計算:n

總計:5*n + 2 * n <= 7 * n = 700000

是o(n)級別的時間複雜度

當然,本題還有其他思路,比如:排序+二分, 雙指標

排序+二分

對於二分,顯然是基於有序陣列,所以第一步就是對三個陣列進行排序,

#include#includeusing namespace std;

const int n = 100010;

int a[n], b[n], c[n];

int main()

if(a[mid] >= b[i])

}pos1 = a[r] < b[i] ? r : r - 1;

l = 0, r = n - 1;

while(l < r)

if(c[mid] > b[i])

}pos2 = c[l] > b[i] ? l : l + 1;

ans += (long long)(n - pos2) * (pos1 + 1);

}cout << ans;

return 0;

}

雙指標

因為對三個陣列排好序後,隨著bi的後移,a中小於bi的個數會增加,c中大於bi的個數會減少,只需要不斷移動指標即可

#include#includeusing namespace std;

const int n = 100010;

int a[n], b[n], c[n];

int main()

cout << ans;

return 0;

}

雖然是兩層迴圈巢狀,但是指標的移動是單向的,並且不發生回溯,所以等價於一層迴圈,還是遠遠小於n^2級別的。

注意:1.由於資料是在100000的範圍,而且出現了乘法,所以要考慮int儲存資料是否會發生溢位,是否需要型別轉換

2.這種列舉型別的題目首先要分析資料範圍,之後根據資料範圍再確定演算法,否則就會做很多的無用功

列舉的一些常用操作

本章將介紹以下幾點 1 如何把其它型別轉換為列舉型別?2 如何把列舉中的值新增到下拉列表中?一 如何把其它型別轉換為列舉型別?我們回顧一下有關字串與數字之間的轉換,如 stringstrvalue 12 intvalue int.parse strvalue 使用int.parse 就可以把字串轉換...

C 中列舉的一些操作

定義乙個cs檔案,把這些內容拷入其中。編譯後執行的如下結果 value none,int 0 value chinese,int 1 value mathematics,int 2 value english,int 3 value physics,int 4 value chemistry,int...

列舉型別,以及highcharts 的一些應用

highcharts api 列舉型別可以有構造方法,但是必須是私有的,只能在列舉的內部訪問,列舉型別 也屬於一種型別,用於定義變數,以限制變數賦值,只能通過列舉名的.值來訪問。好的一點是有個enumset這樣的乙個util類可以建立該列舉型別的set集合,然後可以遍歷這個set集合,再進行賦值 e...