算法分析与设计课程作业第十三周#1#2#3

算法分析与设计课程作业第十三周#1#2#3

这周依旧选了几道动态规划的medium题来做。

494. Target Sum

You are given a list of non-negative integers, a1, a2, …, an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3.
Output: 5
Explanation:

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

There are 5 ways to assign symbols to make the sum of nums be target 3.

Note:
The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.

思路

暴力枚举的话需要O(2^n)的时间复杂度,但仔细观察后发现,如果只修改一条式子的最后一个加减号,暴力枚举要求前面没改的部分也所有重新计算一遍,而事实上前面没改的部分可以记录下来,无需重新计算,这就用到动态规划的思想了。实现就是由前i个数可以计算得到的每个不同结果的不同式子数量,通过对得到的不同的结果加减第i+1个数得到前i+1个数可加减得到的结果,并对应上相应的式子数(加上去),可得到前i+1个数可以计算得到的每个不同结果的不同式子数量,具体状态转移式可在以下代码查看。

代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int size = nums.size();
        int maxsum = 0;
        for(int i = 0; i < size; i++){
            maxsum += nums[i];
        }
        int dp[size][2*maxsum+1];
        for(int i = 0; i < size; i++){
            for(int j = 0; j < 2*maxsum+1; j++){
                dp[i][j]=0;
            }
        }
        dp[0][maxsum+nums[0]]=1;
        dp[0][maxsum-nums[0]]+=1;
        for(int i = 1; i < size; i++){
            for(int j = -maxsum; j <= maxsum; j++){
                if(maxsum+j+nums[i] <= 2*maxsum){
                    dp[i][maxsum+j]=dp[i-1][maxsum+j+nums[i]];

                }
                if(maxsum+j-nums[i] >= 0){
                    dp[i][maxsum+j]+=dp[i-1][maxsum+j-nums[i]];
                }
            }
        }
        return S>maxsum?0:dp[size-1][maxsum+S];
    }
};

416. Partition Equal Subset Sum

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Note:
Each of the array element will not exceed 100.
The array size will not exceed 200.

Example 1:
Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:
Input: [1, 2, 3, 5]

Output: false

Explanation: The array cannot be partitioned into equal sum subsets.

思路

可以用bool dp[i][j]表示从前i个数能否拿出和为j的数,则所求为dp[size-1][sum/2];//size-1为最后一个数的序号,sum/2为和的一半
状态转移式:
dp[i][j] = dp[i-1][j-nums[i]] || dp[i-1][j];
可以这样理解:对第i个数,如果取出来,则dp[i][j] = dp[i-1][j-nums[i]] ;如果不取,则dp[i][j]=dp[i-1][j]。只要任一个为真,都表示前i个数能取出和为j的数。

代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int size = nums.size();
        int sum = 0;
        for(int i = 0; i < size; i++){
            sum += nums[i];
        }
        if(sum%2==1){
            return false;
        }
        bool dp[size][sum+1];
        for(int i = 0; i < size; i++){
            for(int j = 0; j < sum; j++){
                dp[i][j] = false;
            }
        }
        dp[0][0] = true;
        dp[0][nums[0]] = true;
        for(int i = 1; i < size; i++){
            for(int j = 0; j <= sum; j++){
                if(j-nums[i] >= 0)
                    dp[i][j] = dp[i-1][j-nums[i]]||dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j];
            }
        }
        return dp[size-1][sum/2];
    }
};

279. Perfect Squares

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.

思路

考虑一个从1到n的循环,现在的问题是如何从小于n的数的dp[i]状态转移到dp[n]。
能想到的是n要分成完全平方数的和,那么它必须至少能分出一个完全平方数,而这个完全平方数小于等于n,n与这个完全平方数的差也小于等于n,所以dp[n与这个完全平方数的差]是已计算的,所以可以枚举所有小于n的完全平方数,则dp[n] = min{1 + dp[n-square[i]}//square[i]可为所有小于n的完全平方数。

代码

class Solution {
public:
    int numSquares(int n) {
        if(n<4){
            return n;
        }
        int dp[n+1];
        dp[0]=0;
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        for(int i = 2; i*i <= n; i++){
            for(int j = i*i; j <= n&&j<(i+1)*(i+1);j++){
                dp[j]=dp[j-i*i]+1;
                for(int k = i-1; k >=1; k--)
                    dp[j]=min(dp[j],dp[j-k*k]+1);
            }
        }
        return dp[n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值