砝码称重(动态规划详细分析)【蓝桥杯】【十二届】【省赛】【A组】

题目

 

提交记录

题目分析

 分析题目给出的条件:

1<=N<=100,总重不超过100000(即1e5)

如果动态规划做,时间复杂度为1e7,是肯定没问题的。

如果暴力做,复杂度过大,只能得部分的分。

所以,这题很明显是需要动态规划做的,我采用的是通过 f[i][j] 来判断i号砝码能称量出那些重量,砝码号从0~n-1,我们可以从 f[0][ ] 推到 f[n-1][ ],从而得知,在砝码数量为n-1时,能称量出哪些重量。

i表示砝码编号,j表示重量,f[i][j] 表示0~i号砝码都能使用时能否称量出j重量,若能够称量出,则 f[i][j] = 1,否则为0

那么如何从 f[i-1][ ] 推到 f[i][ ] 呢?

我们知道,当只有一个砝码的时候,只能称量出1种重量,所以 f[0][ ] 只有 f[0][w[0]] 这一种状态(w[0]即0号砝码的重量),而 f[1][ ] 多了1个砝码可以使用,即1号砝码,所以可以在 f[0][w[0]] = 1 的基础上得到如下的新状态(f[1][w[1]] = 1在初始化时就可以得到)

新状态状态产生原因
f[1][w[0]] = 1不使用1号砝码
f[1][w[0] + w[1]] = 1增加1号砝码后,放在天平同侧
f[1][abs(w[0] - w[1])] = 1增加1号砝码后,放在天平异侧

同理,可以根据 f[i-1][j] 得到如下新状态:

新状态状态产生原因
f[i][j] = 1不使用 i 号砝码
f[i][j + w[i]] = 1增加 i 号砝码后,放在 j 重量同侧
f[i][abs(j - w[i])] = 1增加 i 号砝码后,放在 j 重量异侧

此处的 j 重量,可以理解为全在天平的某一侧(因为我们得到的是一个相对的重量值)

从而,我们可以从 f[0][ ] 一直推到 f[n-1][ ],代码如下:

for(int i = 1; i < n; i++)
	{
		for(int j = 1; j <= sum; j++)
		{
			if(f[i-1][j])
			{
				f[i][j] = 1;
				f[i][j+w[i]] = 1;
				f[i][int(abs(j-w[i]))] = 1;
			}
		} 
	}

然后累计 f[n-1][ ] 存在多少种状态即可!

源代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 110, W = 1e5+10;

int n, w[N], f[N][W], sum = 0;

int main()
{
	scanf("%d", &n);
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &w[i]);
		f[i][w[i]] = 1;
		sum += w[i];    //得到总重量,即可能称量的重量最大值
	}
	for(int i = 1; i < n; i++)    //枚举i(即求出使用0~i号砝码能得到的所有状态)
	{
		for(int j = 1; j <= sum; j++)    //对所有可能的重量进行进行枚举
		{
			if(f[i-1][j])    //若使用0~i-1号砝码可以称量出j重量
			{    //注意此处的j重量,可以理解为全在天平的某一侧
				f[i][j] = 1;    //那么不使用i号砝码也能称量出j重量
				f[i][j+w[i]] = 1;    //i号砝码放在j重量的同一侧
				f[i][int(abs(j-w[i]))] = 1;    //i号砝码放在j重量的另一侧
			}
		} 
	}
	int cnt = 0;
	for(int i = 1; i <= sum; i++)   //累计f[n-1][]存在的状态,即所有可能称量的重量
		cnt += f[n-1][i];
	printf("%d", cnt);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值