import java.util.Scanner;
//dp的本质就是状态转移
public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int count = 0;// 统计能配出的个数
        int[] w = new int[101];
        boolean[][] dp = new boolean[101][100001];//含义是,砝码数量为i时,能否称出重量j
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long sum = 0L;
        for (int i = 1; i <= n; i++) {
            w[i] = sc.nextInt();// 每个砝码的重量
            sum += w[i];// 砝码最大重量
        }
        for (int i = 1; i <= n; i++) {// 有多少个砝码
            for (int j = 1; j <= sum; j++) {// 砝码最大重量
                if (j == w[i]) {// 能称出砝码的质量就直接赋值为1说明可以称
                    dp[i][j] = true;
                } else {
                    // 分为三种情况1.dp[i-1][j]如果上一次的砝码是1说明可以称出这个重量,我们直接复制下来就行了
                    // 2.dp[i-1][abs(j-w[i])]表示上一次称砝码放天平左边能不能配出这个重量
                    // 3.dp[i-1][j+w[i]]表示上一次称砝码放天平右边能不能配出这个重量
                    // 以上三种情况满足一种就说明此重量可以配出
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][Math.abs(j - w[i])] || dp[i - 1][j + w[i]];
                }
            }
        }
        for (int i = 1; i <= sum; i++) {
            // System.out.print(dp[n][i] + " ");
            if (dp[n][i]) {
                count++;
            }

        }
        System.out.println(count);
    sc.close();
    }

}

这一步的关键是理解如何通过动态规划(DP)来解决砝码问题,尤其是在考虑每个砝码时如何进行状态转移。你需要从多个角度来思考问题:

1. 状态转移的理解:

  • dp[i][j]表示通过前 i 个砝码是否能够称出质量为 j 的物体。
  • 我们的目标是通过状态转移找到是否可以称出某个质量,考虑到砝码可能放在天平的两侧(即放在左边或右边)。

2. 分析三种情况:

  • dp[i-1][j]:表示在没有当前砝码的情况下,前 i-1 个砝码能否称出质量为 j。如果前 i-1 个砝码可以称出质量 j,那么不使用当前砝码 w[i],依然可以称出 j
  • dp[i-1][Math.abs(j - w[i])]:这表示在前 i-1 个砝码能称出质量为 Math.abs(j - w[i]) 时,当前砝码 w[i] 可以放在另一边(天平的一侧),使得总质量为 j。这种情况说明当前砝码是用于调整质量,放在天平的另一侧。
  • dp[i-1][j + w[i]]:这表示如果前 i-1 个砝码能称出质量为 j + w[i],那么当前砝码 w[i] 就可以放在天平的另一侧,使得总质量变为 j

3. 思维方式:

  • 从前一个状态推导当前状态:你通过考虑前一个状态的不同情况来推导当前的可能性。每次考虑当前砝码时,都会更新当前的 dp[i][j],它由之前的 dp[i-1] 状态转移而来。
  • “背包问题”变种:动态规划中的经典背包问题涉及到选择某些物品以达到目标重量。在这个问题中,你需要选择如何分配砝码,使得天平能够达到特定质量,而每个砝码有三种状态(左边、右边、不使用)。

4. 总结:

这步需要你具备:

  • 多角度分析问题:不仅要考虑砝码的重量本身,还要考虑砝码的放置方式(两侧)。
  • 递推思想:通过已知的状态(前 i-1 个砝码的状态),推导出当前状态(前 i 个砝码的状态)。
  • 状态转移的直觉:基于前一个状态的可能性,推测出当前状态的可能性。

 

另外的解法:

//在这个解法中,set其实就是一个去重了的dp数组
//存储的是当前情况下可以测量的重量,而且不重复
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        Set<Integer> set = new HashSet<>();
        int n = scan.nextInt();
        int[] fama = new int[n];

        for (int i = 0; i < n; i++) {
            fama[i] = scan.nextInt();
        }
        //初始化set集合,放入0
        set.add(0);
        for (int i = 0; i < n; i++) {
            //将set集合转化为list集合
            List<Integer> list = new ArrayList<>(set);
            //因为它有下面保存可称重量的过程,所以每一次重新的遍历
            //都是在那些可以称量的重量的基础上进行操作的(体现状态转移的部分,非常巧妙)
            for (int k : list) {
                //将新的砝码与set集合的元素进行加减操作,得出新的砝码秤重
                set.add(k + fama[i]);
                set.add(Math.abs(k - fama[i]));
            }
        }
        //移除0元素
        set.remove((Object)0);
       //输出set集合大小,也就是秤重方案数
        System.out.println(set.size());
        scan.close();
    }
}

 

posted on 2025-05-22 19:22  fafrkvit  阅读(14)  评论(0)    收藏  举报