分治系列——312. Burst Balloons[hard]03-09

題目描述

Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent.

Find the maximum coins you can collect by bursting the balloons wisely.

Note: 
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 167

    nums = [3,1,5,8] --> [3,5,8] -->   [3,8]   -->  [8]  --> []
   coins =  3*1*5      +  3*5*8    +  1*3*8      + 1*8*1   = 167
大意:尋找戳破氣球的最優解,使得分最高,計分:nums[left] * nums[i] * nums[right]


解題思路一(超時):

遍歷所有計分可能,與當前分數比較,大的話就把新得到的分數替換原來分數,運用for循環遍歷所有可能性。很遺憾,超時了,在數組大於10的時候明顯效率低下。(但是我還是想把源碼貼出來)


/*void burst(vector<int> nums, int i, int& result, int& really) {

	int re = 0;
	int left = (i - 1 == -1) ? 1 : nums[i - 1];
	int right = (i + 1 == nums.size()) ? 1 : nums[i + 1];

	re += (left * right * nums[i]);

	result += re;

	nums.erase(nums.begin() + i);

	
		
		for (int u = 0; u < nums.size(); u++) {

			burst(nums, u, result,really);

		}
	

		if (nums.size() == 0 && result > really) {
			really = result;
			
		}

		result -= re;
}

int maxCoins(vector<int>& nums) {
	if (nums.size() == 0)
		return 0;

	int final001 = 0;

	for (int i = 0; i < nums.size(); i++) {

		int result = 0;
		
		burst(nums, i, result,final001);


	}

	//sort(final001.begin(), final001.end());

	return final001;

}*/


解題思路二:

借鑑其他博客的方法,用到了動態規劃:[動態規劃背後的基本思想非常簡單--大致上,若要解一個給定問題,我們需要解其不同部分(即子問題),再合併子問題的解以得出原問題的解。 通常許多子問題非常相似,爲此動態規劃法試圖僅僅解決每個子問題一次,從而減少計算量: 一旦某個給定子問題的解已經算出,則將其記憶化存儲,以便下次需要同一個子問題解之時直接查表。 這種做法在重複子問題的數目關於輸入的規模呈指數增長時特別有用。引用地址:http://blog.csdn.net/carson2005/article/details/22092969]


遞推的公式需要仔細想一下,需要注意的是氣球被打掉就不會再被計算了。我們假設f(i, j)表示一個以i+1爲起始位置,j-1終止的sub array能得到的最多coin數量。正着猜第一個氣球打哪一個不好猜,於是我們可以倒着猜。假設打的最後一個氣球是第k個氣球(在[i, j]範圍內),也就是說,從第i+1到k-1這個範圍內的氣球以及從k+1到j-1這個範圍內的氣球此時已經全被打掉了。那麼可以知道,當打掉最後一個氣球k時,所得到的coin的數量就是: f(i, k) + f(k, j) + nums[i]* nums[k] * nums[j]。我們猜測每一個k的位置然後取其中的最大值。

引用地址:http://www.hihuyue.com/hihuyue/codepractise/leetcode/leetcode312-burst-balloons]


下面是我的代碼以及解釋:


int maxCoins(vector<int>& nums) {
	int many = nums.size();

	//(1)
	nums.insert(nums.begin(), 1);
	nums.insert(nums.end(), 1);

	//(2)
	//dp[i][j] = max(dp[i][j], nums[i]*nums[k]*nums[j] + dp[i][k-1] + dp[k+1][j]) k的範圍爲[i,j] 
	vector<vector<int> > dp(many+2,vector<int>(many+2,0));
	for (int count = 2; count <= many+1; count++) {
		//(3)
		for (int i = 0; i <= many - count + 1; i++) {
			int j = i + count;

			for (int k = i + 1; k < j; k++) {
				dp[i][j] = max(dp[i][j], nums[i] * nums[k] * nums[j] + dp[i][k] + dp[k][j]);

			}
		}
		
	}

	return dp[0][many+1];//(4)
}



註釋(1):

在數組左右加上1,相當於把nums[-1],nums[n]同時考慮進來,方便許多

註釋(2):

構造了一個二維數組,利用dp[i][j]表示nums在(i,j)裏的最大分,也就是動態規劃裏,用一張表記錄下所有問題的解,以便下次複用。然後,(i,j)從區間裏只包含一個數擴大到包含整個數組,結果也就出來了。k 屬於 (i,j),也就是最後一個被戳破的,它戳破前,(i,k)和(k,j)已經無氣球了,i,j是它的left & right。

註釋(3):

i,j,k 的取值。爲了推出 i,j,可以考慮數組只剩下一個數的情形(假設數組原來size = 4),那麼,要遍歷的k,就可能是 1,2,3,4(0,5都是後來加的1),那麼對應的,k = 1,i = 0,j = 2; k = 2, i = 1,j = 3……,還有最後一種情況,1234同時存在,i=0,j = 5 依次類推得出:

int i = 0; i <= many - count + 1; i++

int j = i + count;


還有一點,是爲什麼 + dp[i][k] + dp[k][j], 而不是 + dp[i][k-1] + dp[k+1][j]

因爲 k >= i + 1, k < j,是一個開區間,如果換成後者,k-1,k+1兩個元素都將被排除,結果錯誤


心得:

1)用二維數組表示區間

2)vector的構造方法:

vector<vector<int> > dp(many+2,vector<int>(many+2,0));

3)動態規劃

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