ACM-學習記錄-尺取法
阿新 • • 發佈:2020-12-15
題目
給定一個數組和一個數s,在這個陣列中找一個區間,使得這個區間之和等於s。
例如:給定的陣列int x[14] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};和一個s = 15。那麼,可以找到的區間就應該有0到4, 3到5, 6到7.(注意這裡的下標從0開始)
思路
對於這樣的題,不用任何技巧就可以跑出結果,例如下面這個方法可能是大多數人能夠想出來的:
先用一個數組sum[i]存放前i個元素的和,其實現用的是”遞推思想“,注意,在程式設計中”遞推“的思想用的特別多,一定要習慣這種思維方式。
sum[0] = x[0];//x為給定的原陣列 for(int i = 1; i < n; i++){ sum[i] += sum[i-1];//遞推思想 }
然後通過兩層迴圈求解
for(int i = 0; i < n; i++)
for(int j = n-1; j >= 0; j--){
if(sum[j]-sum[i]==s) printf("%d---%d\n", i, j);
}
上面的方法當然是可行的,但是複雜度太高,有一個演算法可以將其複雜度降為O(n)。這就是”尺取演算法“。
尺取法:顧名思義,像尺子一樣取一段,借用挑戰書上面的話說,尺取法通常是對陣列儲存一對下標,即所選取的區間的左右端點,然後根據實際情況不斷地推進區間左右端點以得出答案。之所以需要掌握這個技巧,是因為尺取法比直接暴力列舉區間效率高很多,尤其是資料量大的。
那麼,用”尺取法“做上面這道題思路應該是這樣的:
其實,這種方法很類似於蚯蚓的蠕動。
1)用一對腳標i, j。最開始都指向第一個元素。
2)如果區間i到j之和比s小,就讓j往後挪一位,並把sum的值加上這個新元素。相當於蚯蚓的頭向前伸了一下。
3)如果區間i到j之和比s大,就讓sum減掉第一個元素。相當於蚯蚓的尾巴向前縮了一下。
4)如果i到j之和剛好等於s,則輸入。
實現
#include<iostream> #include<cstdio> using namespace std; void findSUM(int *A, int n, int s){ int i = 0, j = 0; int sum = A[0]; while(i <= j && j < n){ if(sum >= s){ if(sum == s) printf("%d---%d\n", i, j); sum -= A[i]; i++; } else{ j++; sum += A[j]; } } } int main(){ std::ios::sync_with_stdio(false); std::cin.tie(0); int m; int x[14] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; cin >> m; findSUM(x, 14, m); return 0; }