[持续更新]动态规划题集

23 篇文章 1 订阅
4 篇文章 0 订阅

蓝桥杯2021年第十二届省赛真题-砝码称重

时间限制: 1Sec 内存限制: 128MB 提交: 1797 解决: 590

题目描述

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · , WN。
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边。

输入

输入的第一行包含一个整数 N。
第二行包含 N 个整数:W1, W2, W3, · · · , WN。

输出

输出一个整数代表答案。

样例输入

3
1 4 6

样例输出

10

提示

【样例说明】
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 6 4 (天平一边放 6,另一边放 4);
3 = 4 1;
4 = 4;
5 = 6 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 1;
10 = 4 + 6;
11 = 1 + 4 + 6。

【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ N ≤ 15。
对于所有评测用例,1 ≤ N ≤ 100,N 个砝码总重不超过 100000。

状态转移方程就是:
dp[i]的全部可能=dp[i-1]+和以前的加减新组合

代码实现:

import java.util.Scanner;
public class 砝码称重 {
    static int N;
    static int[] W;
    static int COUNT;

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        W = new int[N];
        int sum = 0;
        for (int i = 0; i < N; i++) {
            sum += W[i] = scanner.nextInt();
        }
        COUNT = sum;
        //Arrays.sort(W);
        dpFaMa();
    }

    /*
    状态转移方程就是:
    dp[i]的全部可能=dp[i-1]+和以前的加减新组合
    数组dp[i]的大小为全部砝码的总和  COUNT
     */
    public static void dpFaMa() {
        boolean[][] dp = new boolean[W.length][COUNT + 1];
        for (int i = 0; i < W.length; i++) {
            //对于dp数组的初始化  对于目标为0的配重 都置为true
            // 对于每一行和砝码重量相同的目标配重也置为true
            dp[i][0] = true;
            dp[i][W[i]] = true;
        }
        //从第二块砝码开始递推
        for (int i = 1; i < dp.length; i++) {
            //第一列配重为0跳过
            for (int j = 1; j < COUNT + 1; j++) {
                //加当前砝码w[i]之前,就存在的;
                if (dp[i - 1][j]) {
                    dp[i][j] = true;
                }

                //情况一:砝码和j在一起
                if (W[i] + j <= COUNT && dp[i - 1][W[i] + j]) {
                    //当前砝码和目标都放到同一边,然后有已经可以测的重量在另一侧
                    //还有个限制,就是当前砝码,和当前测量j之和不能超过总重,即数组的总重;
                    //如果有的话,当前j就能true
                    dp[i][j] = true;
                }

                //情况二:砝码和j分开
                if (dp[i - 1][Math.abs(j - W[i])]) {
                    //j可以被当前砝码和以前的砝码组成,
                    //或者j加以前的砝码等于当前砝码;
                    dp[i][j] = true;
                }
            }
        }
        int sum = 0;
        for (int i = 1; i <= COUNT; i++) {
            if (dp[N - 1][i]) {
                sum++;
            }
        }
        System.out.println(sum);
    }
}

53.最大子数组和

题解:

package leetcode;

/**
 * @Description:
 * @Author Ladidol
 * @Date: 2022/3/26 20:40
 * @Version 1.0
 */
public class _53_最大子数组和 {
    public static void main(String[] args) {
        int[] nums = {-2,1,-3,4,-1,2,1,-5,4};
        System.out.println(new 最大子数组和().maxSubArray(nums));
    }

}
/**
 * @Description:

 * @Author: ladidol
 * @Date: 2022/3/26 21:08
 * @Param:
 * @Return:
 */
class 最大子数组和 {
    public int maxSubArray(int[] nums) {
        //只有当以当前节点结尾的子节点,这样来考虑的话,才有dp的影子;
        //当前节点是结尾or开始,max(dp[i]+nums[i],nums[i]);
        //这里能取到当前节点的最优解了,但是下一个节点的最优解就要和最优解maxOld比较
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            dp[i] = Math.max(dp[i-1] + nums[i],nums[i]);
            if (dp[i] > max){
                max = dp[i];
            }
        }
        return max;

    }
}

70.爬楼梯和青蛙跳阶一样的

package leetcode;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description:
 * @Author Ladidol
 * @Date: 2022/3/25 15:00
 * @Version 1.0
 */
public class _70_爬楼梯 {
    public static void main(String[] args) {
        System.out.println("爬十阶楼梯有 " + new 爬楼梯().climbStairs(10));
    }
}
class 爬楼梯 {
    public Map<Integer, Integer> note = new HashMap<> ();
    public int climbStairs(int n) {
        if( n <= 2){
            return n;
        }
        //按这种,代表每一次分叉都会开启一次递归,有重复的递归
//        return climbStairs( n - 1) + climbStairs( n - 2);

        //按这种,代表每一个台阶都只开启一次递归
        if (note.containsKey(n)){
            return note.get(n);
        }else {
            note.put(n,climbStairs(n-1)+climbStairs(n-2));
            return note.get(n);
        }

    }
}

300.最长递增子序列

package leetcode;

/**
 * @Description:
 * @Author Ladidol
 * @Date: 2022/3/25 16:01
 * @Version 1.0
 */
public class _300_最长递增子序列 {
    public static void main(String[] args) {
        int[] nums = {10,9,2,3,7,101,18};
        System.out.println(new 最长递增子序列().lengthOfLIS(nums));
    }
}
class 最长递增子序列 {
    //dp(i) =max(dp(j))+1,
    // 存在j属于区间[0,i-1],并且num[i]>num[j]。
    public int lengthOfLIS(int[] nums) {
        int[] dp = new int[nums.length];
        int maxLen = 0;
        for (int i = 0; i < nums.length; i++) {
            dp[i] = 1;
            for (int j = 0; j < i; j++) {
                if(nums[i]>nums[j]){
                    //这里只能这样,因为,如果是元dp[i],就不加一
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            maxLen = Math.max(dp[i],maxLen);
        }
        return maxLen;
    }
}

121.买股票最佳时机

package leetcode;

/**
 * @Description:
 * @Author Ladidol
 * @Date: 2022/3/26 19:36
 * @Version 1.0
 */
public class _121_买股票最佳时机 {
    public static void main(String[] args) {
        int[] nums1 = {7,1,5,3,6,4};
        int[] nums2 = {7,6,4,3,1};
        System.out.println(new 买股票().maxProfit(nums1));

    }
}
class 买股票 {
    public int maxProfit(int[] prices) {

        //数组中存有当前的现金;
        //每一个天有两个状态,持有股票1和没有持有股票0
        //持有股票的情况来自于,今天买的or昨天及之前就买了的,然后天持有的股票一定是今天以前最低价买来的;
        //没有股票的情况来自于,今天卖出去了股票or昨天及之前就卖了的


        //dp[i][0]没持股票,dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
        //dp[i][1]持有股票,dp[i][1]=max(dp[i-1][1],-prices[i])

        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for(int i = 1 ; i< n; i++){
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1]=Math.max(dp[i-1][1],-prices[i]);
        }
        return dp[n-1][0];

    }
}

欢迎点赞关注哦!
也欢迎到访我的博客!小小的博客传送门!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兴趣使然的小小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值