12届蓝桥杯省赛-砝码称重

目录

前言:

正文:

一、题干:

二、思路:

参考博客:

后记:


前言:

        在这一篇文章中,我们主要介绍“砝码称重”这道题的解法

正文:

一、题干:

【问题描述】

        你有一架天平和 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。

二、思路:

        这道题中,可以发现放入第一个砝码的时候,只能称出一个值,放入第二个砝码的时候,可以只放第二个砝码称出一个值,可以把第一个放左边第二个放右边称出一个值,可以把第一个放右边边第二个放左边又称出一个值。而第三个砝码出现的时候,我们可以根据之前得到的值加上砝码来得到新的值。总而言之,就是动态规划。

        这样题目就化简为了如何用旧的值推出新的值,我们用一个一维数组来存储可以表示出的值,对每一个新的值引入的时候,我们做以下操作来推出新的值(设旧的值为j,新的值为a,数组名为b)

        b[j+a]=1;(将j和a看作砝码的话,就是j和a都放右边(左物右码))

        b[j-a]=1;(将j和a看作砝码的话,就是j放右边a放左边)

        b[a-j]=1;(将j和a看作砝码的话,就是j放左边a放右边)

        只不过这样子会放出负数,而数据范围说了砝码总重不超过100000,所以最后操作的时候我们要把所有值都加100000防止访问到负数。

        以题中所给的样例为例进行演算:

        放入一个砝码重量为1时(w为可以称出的重量):

        w=1;

        放入二个砝码重量为1,4时:

        w=1+4=5;(用w=1来求)

        w=1-4=-3;

        w=4-1=3;

        w=4;

        放入三个砝码重量为1,4,6时:

        w=1+6=7;(用w=1来求)

        w=1-6=-5;

        w=6-1=5;

        w=5+6=11;(用w=1+4=5来求)

        w=5-6=-1;

        w=6-5=1;

        w=-3+6=3;(用w=1-4=3来求)

        w=-3-6=-9;

        w=6-(-3)=9;

        w=3+6=9;(用w=4-1=3来求)

        w=3-6=-3;

        w=6-3=3;

        w=4+6=10;(用w=4来求)

        w=4-6=-2;

        w=6-4=2;

        w=6;

        上面所有不重复的正数(也就是标蓝的)数量就是答案:10。

        思路已经明了,所以程序呼之欲出:

#include<bits/stdc++.h>
using namespace std;

int main(int argc, char const *argv[])
{
    int b[200001];//是否被称出,以及是前几个称出的
    memset(b,0,sizeof(b));
    int n;
    cin>>n;
    int a;
    for (int i = 1; i <= n; i++)
    {
        cin>>a;
        for (int j = 0; j <= 200000; j++)
        {
            if(b[j]!=i&&b[j]!=0)
            {
                if(b[j+a] == 0)b[j+a]=i;
                if(b[j-a] == 0)b[j-a]=i;
                if(b[200000+a-j] == 0)b[200000+a-j]=i;
            }
        }
        b[a+100000] = i;
    }
    int sum = 0;
    for (int i = 100001; i <= 200000; i++)
    {
        if(b[i]!=0)
        {
            sum++;
        }
    }
    cout<<sum;

    return 0;
}

其中

    for (int i = 1; i <= n; i++)
    {
        cin>>a;
        for (int j = 0; j <= 200000; j++)
        {
            if(b[j]!=i&&b[j]!=0)
            {
                if(b[j+a] == 0)b[j+a]=i;
                if(b[j-a] == 0)b[j-a]=i;
                if(b[200000+a-j] == 0)b[200000+a-j]=i;
            }
        }
        b[a+100000] = i;
    }

        这一部分就是核心部分,说明一下,b不像前面样例中存的是0和1,在这个程序中存的是整型变量,用来表示它可以由前b[j]个砝码称出,而b[j]!=i是为了防止重复使用一个砝码,举个例子:

        b[4+6]=3;

        b[4+6+6]=3;

        b[4+6+6+6]=3;

        如此就可以在使用i个砝码的时候,只使用前i-1个砝码了。

        另外,如果称出0的重量算数吗?(比如左边放5,右边放2+3)答案是不算哈~,所以在求结果的时候要把0给排除掉。

参考博客:

https://blog.csdn.net/asbbv/article/details/117253522

后记:

感谢各位读到这,虽然是AC了但是这道题绝对有更简洁更快的方法,果然还是太菜了唉QAQ

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值