动态规划--砝码称重

题目描述

你有一架天平和 N 个砝码, 这 N 个砝码重量依次是 1,2,⋯ ,W1​,W2​,⋯,WN​ 。 请你计算一共可以称出多少种不同的重量?

注意砝码可以放在天平两边。

输入格式

输入的第一行包含一个整数 N 。

第二行包含 N 个整数: 1,2,3,⋯ ,W1​,W2​,W3​,⋯,WN​ 。

输出格式

输出一个整数代表答案。

输入输出样例

输入 #1复制

3
1 4 6

输出 #1复制

10

说明/提示

【样例说明】

能称出的 10 种重量是: 1、2、3、4、5、6、7、9、10、111、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%50% 的评测用例, 1≤N≤15 。

对于所有评测用例, 1≤N≤100,N 个砝码总重不超过 10^5。

import java.util.Scanner;

public class Main{
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		int[] arr = new int[n + 1];
		int sum = 0;
		for(int i = 1;i <= n;i++) {
			arr[i] = scanner.nextInt();
			sum += arr[i];
		}
		int[] dp = new int[sum + 1];
		//dp[j]是砝码是否可以称出j重量,可以为1,不可以为0
		int ans = 0;
		dp[0] = 1;//即什么也不放
		for(int i = 1;i <= n;i++) {//放在同一侧
			for(int j = dp.length - 1;j >= arr[i];j--) {
				if(dp[j - arr[i]] == 1 && dp[j] == 0) {
					dp[j] = 1;
					ans++;
				}
			}
		}
		for(int i = 1;i <= n;i++) {//放在不同侧
			for(int j = 1;j <= sum - arr[i];j++) {
				if(dp[j + arr[i]] == 1 && dp[j] == 0) {
					dp[j] = 1;
					ans++;
				}
			}
		}
		System.out.print(ans);
	}
}

题解分析:这里的dp数组与之前题目中的dp数组不同,这里的dp[j]表示这些砝码是否可以称出重量j,如果是则dp[j] = 1,否则dp[j] = 0。那这与动态规划又有什么关系?我们假设此时有个重量j,那么dp[j]是否为1,一定是取决于dp[j - arr[i]]是否为1,只有dp[j - arr[i]]这个重量是存在的,那么才有放置arr[i]后j重量的出现,这里现在的状态取决于之前的状态体现了动态规划的思想。

动态规划五部曲

1.dp数组以及其下标的含义:dp[j]表示是否可以称出质量j,可以为1,不可以为0

2.递推公式:因为把砝码放在天平的同一侧和不同侧是完全不同的,因此需要分别计算,放在同一侧时:如果未放砝码arr[i]时的质量存在,那么放入砝码后的质量存在,即若有dp[j + arr[i]] == 1,那么dp[j] = 1;同理,放在不同侧时,若未放砝码arr[i]时质量dp[j + arr[i]] == 1,则dp[j] = 1.

3.dp数组如何初始化:放第一个砝码时需要知道未放砝码时的dp,未放砝码即质量为0,可以称出初始化dp[0] = 1。

4.遍历顺序:外层循环遍历砝码,内层循环遍历重量。需要注意的是若砝码放在同一侧,内层循环应该从后往前,若砝码放在不同侧,内层循环应该从前往后。

5.打印dp数组:用于debug自查代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值