LeetCode 312. Burst Balloons

          本题大概的题意是:给定一个数组,每次删除一个数字,在每次删除的同时得到它与其两边的数字的积的分数(两边没有数字则不乘),删除完之后原来两边的数字就变的相邻了。求可能的最高的分数。

         一开始我想用的是贪心的做法:用链表存储整个数组,每次删除不再边界的最小的数。但是这样的方法毫无依据,果然WA了。

          接着想到了用递归的方法,每次枚举最后被删除的数字。为了方便描述,设被删除的数字为a。每次枚举的时候用a乘以两端以外的值(例如,一开始两端意外的值为1和1),用它们的积加上分成两部分的用递归算出来的值(注意此时a已经成为了左边部分的右端以外的值,并且是右边部分的左边以外的值)。递归终点为该部分长度小于1.

递归的过程需要记录下左右端点的下标,和左右端点以外的值。

          然而这样复杂度还是太高了,复杂度达到了O(n!)级别。经过具体提交发现当n大于等于20的时候就会超时。一开始我想能不能用记忆化来减小时间复杂度。但变量有四维,数组太大。于是我在想能否有办法能够剪枝,经过思考后无果。

         后来我才发现,传递左右端点以外的值完全是没有必要的,我太拘泥于它们不在左右端点隔壁的情况。但只要仔细思考,它们在原来的数组中一定处于左右端点的隔壁。这样变量降到了二维,可以使用记忆化搜索。

         具体实现需要重新构建一个左右端点为1的数组。代码如下:

# include <iostream>
# include <cstdio>
# include <cstring>
# include <vector>
using namespace std ;

# define INF 100000000

const int maxn = 550 ;
int d[maxn][maxn] ;

class Solution {
public:
    int maxCoins(vector<int>& nums) {
    	vector<int> nums2 ; 
    	nums2.push_back(1) ;
		for (int i=0; i<nums.size(); i++) nums2.push_back(nums[i]) ;
		nums2.push_back(1) ;   
		memset(d, -1, sizeof(d)) ;
		
        return mydfs(nums2, 0, nums2.size()-1) ;
    }
    
    
    int mydfs(vector<int>& nums, int x, int y) {
        if (x >= y - 1) return 0 ;
        if (x == y - 2) return nums[x]*nums[x+1]*nums[y] ;
        
        if (d[x][y] != -1) return d[x][y] ;
        int ans = -INF ;
	
		for (int i=x+1; i<y; i++) { 
	       int curvalue =  mydfs(nums, x, i) + mydfs(nums, i, y) + nums[x]*nums[i]*nums[y] ;
		   ans = max(ans, curvalue) ;
		}
		return d[x][y] = ans ;
    	
    }
    
};
          其中使用到了记忆化搜索。

  

         而用dp的方法也是可行的,递推公式如下:

        当j < i+1 时   d[j][i] = max(d[j][i],   d[j][k] + d[k][i] + nums[i] * nums[k] * nums[j] ) ;    j < k < i 

        否则, d[j][i] = 0 ;

        具体代码如下,注意j的枚举顺序:

       

# include <iostream>
# include <cstdio>
# include <cstring>
# include <vector>
using namespace std ;


# define INF 100000000

const int maxn = 550 ;
int d[maxn][maxn] ;

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        vector<int> a ;
        a.push_back(1) ;
		for (int i=0; i<nums.size(); i++) a.push_back(nums[i]) ; 
		a.push_back(1) ;
		
		int n = a.size() ;
		memset(d, -1, sizeof(d)) ;
		for (int i=0; i<n-1; i++) d[i][i+1] = 0 ;
		for (int i=0; i<n; i++) d[i][i] = 0 ;
		
		for (int i=0; i<n; i++) {
			for (int j=i-1; j>=0; j--) {
				for (int k=j+1; k<i; k++) {
					d[j][i] = max(d[j][i], d[j][k] + d[k][i] + a[i]*a[k]*a[j]) ;
				}
			}
		}
		return d[0][n-1] ;
    }  
};


            无论是记忆化搜索还是dp,算法的复杂度均为O(n3)。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值