不斷減少時間複雜度的一個例子

        問題:給出N張寫有數字(k1, k2, ..., kn)的牌 和 一個數字M, 從中抽4次(每抽完一次要放回), 判斷是否存在抽取4次牌上數字的和爲M的組合是否存在。

                   1 <= n <= 1000,  1 <= m <= 10^9, 1 <= ki <= 10^6 

        輸入:n = 3, m = 10, k = {1, 3, 5}        n = 3, m = 9, k = {1, 3 , 5}

        輸出:yes(1, 1, 3,  5)                            no(不存在取4次和爲9的情況啦)

        

        (1)最容易想到的方法當然是暴力枚舉啦, 那麼抽取4次,那麼就需要4層循環了,時間複雜度爲O(n^4)

	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			for (k = 0; k < n; k++) {
				for (l = 0; l < n; l++) {
					if (m == cards[i] + cards[j] + cards[k] + cards[l]) {
						flag = true;
						break ;
					}
				}
			}
		}
	} // O(n^4)
        題目輸入範圍可以到1000, 那麼上面的將會超時啦。


         (2)要判斷是否m = cards[i] + cards[j] + cards[k] + cards[l], 那麼可以判斷cards[l] = m - cards[i] - cards[j] - cards[k]是否存在(二分查找)。O(n^3*logn)

int bin_search(int* arr, int left, int right, int key) {
	int mid;

	while (left <= right) {
		mid = (left + right)>>1;
		if (arr[mid] == key) {
			return mid;
		} else if (arr[mid] > key) {
			right = mid - 1;
		} else {
			left = mid + 1;
		}
	}

	return -1;
} 

	sort(cards, cards + n);
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			for (k = 0; k < n; k++) {
				key = m - cards[i] - cards[j] - cards[k];
				if (!flag && bin_search(cards, 0, n, key) > 0) {
					flag = true;
				}
			}
		}
	} // O(n^3*logn)

          時間複雜度減小了, 但還是不能滿足哦。


          (3)按照上面的思路, 那麼我們可以查找cards[l] + cards[k] = m - cards[i] + cards[j]哦, 那麼需要一個n*n的數組來保存cards[i] + cards[j]的和哦。O(n^2*logn^2)

	k = 0;
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			nnCards[k++] = cards[i] + cards[j];
		}
	}
	sort(nnCards, nnCards + k);
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			key = m - cards[i] - cards[j];
			if (!flag && bin_search(nnCards, 0, k, key) > 0) {
				flag = true;
			}
		}
	} // O(n^2*logn)
           好了, 時間複雜度降爲O(n^2*logn^2)了, 但空間複雜度增加了, 需要一個存放n*n的數組, 那麼1000*1000的話還是有點大了吧, 時間換空間?

           這也算一種技巧,暴力搜索的時候多想想能不能降低時間複雜度吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章