第十二届蓝桥杯真题砝码称重dp

问题描述

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。

请你计算一共可以称出多少种不同的正整数重量?

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

输入格式

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

第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。

输出格式

 输出一个整数代表答案。

数据范围

 对于 50% 的评测用例,1≤N≤15

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

输入样例:

3

1 4 6

输出样例:

10

解题思路:

用dp法,分析状态和状态转移的过程,类似于01背包问题,多了一个分支

状态表示:

题目保证砝码不大于100,总砝码重不超过100000,则状态最多为100*100000个,设dp[102][100005]足以表示所有的状态。w[102]表示每个砝码的重量。

状态的意义:

dp[i][j]!=0表示前i个砝码可以称出j重量,反之dp[i][j]=0表示前i个砝码不可以称出j重量,所以首先要将状态全部设置为0.

边界条件:

前0个砝码可以称出0重量,即dp[0][0]=1

状态转移:

dp[i][j]的状态可以由三个方向转移而来,即:

1)让第i个放在天平的重的那边(假定为右边)

2)让第i个不放上天平

3)让第i个放在天平的轻的那边(假定为左边)

由1可得到dp[i][j]可由dp[i-1][j-w[i]]或者dp[i-1][w[i]-j]得来,这里为了避免数组索引为负值用abs()处理,即dp[i][j]可由dp[i-1][abs(j-w[i])]得来

由2可得到dp[i][j]可由dp[i-1][j]得来

由3可得到dp[i][j]可由dp[i-1][j+w[i]]得来

由以上分析可得到,只要dp[i - 1][j] , dp[i - 1][j + w[i]] , dp[i - 1][abs(j - w[i])]任意一项可以实现,则dp[i][j]也可以得到

状态转移方程为:

dp[i][j]=dp[i - 1][j] + f[i - 1][j + w[i]] + f[i - 1][abs(j - w[i])]或者

dp[i][j]=max(dp[i - 1][j] , f[i - 1][j + w[i]] , f[i - 1][abs(j - w[i])])

dp[i][j]只要非0,就表示前i个砝码能称出j重量

本题目的就是求n个砝码能表示出的重量 ,遍历dp[n][j] ,数一数有多少个非0值即可,

代码如下(C++):

#include<iostream>
using namespace std;
typedef long long ll;
int dp[102][100005];//前i个砝码可以称出质量j 
int w[102];
int n;
int sum = 0;
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
		sum += w[i];
	}
	dp[0][0] = 1;//前0件 可以称出重量为0
	for (int i = 1; i <= n; i++) {   //有n*sum个状态
		for (int j = 0; j <= sum; j++) {//这里j不能从1开始 必须从0 第一件物品不可能称出0 但是前i件可能称出0
			dp[i][j] = dp[i - 1][abs(j - w[i])] + dp[i - 1][j + w[i]] +dp[i - 1][j];
		}
	}
	int ans = 0;
	for (int i = 1; i <= sum; i++) {
		if (dp[n][i] != 0) {
			ans++;
		}
	}
	cout << ans;
	return 0;
}

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值