吹爆气球问题

之前看到这个问题,没有头绪,参考了http://www.tuicool.com/articles/NZBNZ3Z的解法,里面循环的问题稍微理清了一下。

废话不说,先上题。

有n个气球,编号为0到n-1,每个气球都有一个分数,存在nums数组中。每次吹气球i可以得到的分数为 nums[left] * nums[i] * nums[right],left和right分别表示i气球相邻的两个气球。当i气球被吹爆后,其左右两气球即为相邻。要求吹爆所有气球,得到最多的分数。

注释:

(1) 你可以假设nums[-1] = nums[n] = 1

(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

给出[3, 1, 5, 8]

返回167

 

这个DP问题可以看作是,对于某个特定的气球,其得分等于 吹爆两边的气球,再吹中间的那个气球。

我们定义一个dp[i][j], 表示吹爆编号从 i 到 j 的气球所得的最大分数。

dp[i][j] = Max{dp[i][k-1] +  dp[k+1][j] + nums[i-1] * nums[k] * nums[j+1]}, 式中i+1 <= k <= j-1,即至少要求有3个气球存在(不考虑辅助的nums[-1] 和 nums[n);

对于j = i + 1来说,dp[i][j] = max{先吹爆i 再吹爆j, 先吹爆j 再吹爆i }

 

public int booloon(int[] nums){
        		int n = nums.length;
        		int[] arr = new int[nums.length + 2];
        		arr[0] = 1;
        		arr[arr.length - 1] = 1;
        		for(int i = 0; i < nums.length; i++)
        			arr[i+1] = nums[i];
        		
        		int[][] dp = new int[n+2][n+2];
        		// 边界条件:只吹爆编号为i的气球的得分
        		for(int i = 1; i <= n; i++)
        			dp[i][i] = arr[i-1] * arr[i] * arr[i+1];
        		

        		for(int i = n; i >= 1; i--)
        			for(int j = i+1; j <= n; j++){
                                       // 只有2个的情况        				
        				dp[i][j] = Math.max(dp[i][j-1] + arr[i-1] * arr[j] * arr[j+1],
        						dp[i+1][j] + arr[i-1] * arr[i] * arr[j+1]
        						); 
                                      // 至少3个的情况
        				for(int k = i+1; k <= j-1; k++)
        					dp[i][j] = Math.max(dp[i][j], dp[i][k-1] + dp[k+1][j] + arr[k] * arr[i-1] * arr[j+1]);
        			}
        		
        		
        		return dp[1][n];
        	}

  

posted @ 2017-05-15 10:33  贾斯彼迭  阅读(547)  评论(0编辑  收藏  举报