遞增三元組——用統計量(字首和)化簡列舉
題目要求很簡單,給出三個陣列,找出滿足題意的所有組合的個數。
暴力列舉
剛開始沒在意,直接暴力列舉
#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...