• 砝码称重


    题目

    给定几种重量的砝码,数量不限,判断是否可以称出任意重量的物品。有时间空间限制,时间是1秒内,也就是不能用笨重的穷举法。输入规则,第一行输入一个整数,表示有几个砝码;然后挨个输入每个砝码的重量。可以称重的物品都是整数。能,就输出YES;不能,输出NO。

    示例1

    1

    1

    YES

    第一个1是有一种重量的砝码,第二个1是这个砝码是多重。这样肯定可以称出任意重量的物品。

    示例2

    2

    2 3

    YES

    第一个2是有两种重量的砝码,第二行2 3表示每种砝码重量是2和3,那么也是可以称出任意重量的物品。

    示例3

    2

    2 4

    NO

    这一组只能得到2N物品的重量,无法称出奇数物品的重量。

    思考

    什么条件可以称出任意重量的物品呢?考虑到素数的原因,那么只有得到重量为1的砝码,才可以实现目标。

    第一个思路,给定的砝码相互相加,直到有两个重量相差为1。但是这里有一个边界问题,什么时候可以结束呢?相加可以无上限,放弃。

    第二种思路,让砝码相减,直到算出1。这种方法貌似是可行的,并且可以很好的判断边界,就是每次相减都会产生一个小于最大砝码值,大于等于0,并且不在已经得出的砝码重量里面的新数值。如果一次循环没有得到新数值,也就是每次相减得到的数都已经存在了,那么结束。拿上面的示例举例,示例1,因为直接得到1,可以;示例2,3-2=1,可以;示例3,4-2=2 2-2=0,只能得到0 2 4,没有1,所以不可以。代码如下

    bool proone(map<int, bool>& fmweight)
    {
        if (fmweight[1])
        {
            return false;
        }
        vector<int> tmppronum;
        for (auto iter = fmweight.rbegin(); iter != fmweight.rend(); iter++)
        {
            if (!iter->second)
            {
                continue;
            }
            auto iter1 = iter;
            iter1++;
            for (; iter1 != fmweight.rend(); iter1++)
            {
                if (!iter1->second)
                {
                    continue;
                }
                int tmpnum = iter->first - iter1->first;
                if (fmweight.count(tmpnum) == 0 || !fmweight[tmpnum])
                {
                    tmppronum.emplace_back(tmpnum);
                }
            }
        }
        if (!tmppronum.empty())
        {
            for (auto& iter : tmppronum)
            {
                fmweight[iter] = true;
            }
            return true;
        }
        return false;
    }
    bool test()
    {
        int fmnum = 0;
        cin >> fmnum;
        map<int, bool> fmweight;
        fmweight[1] = false;
        for (size_t i = 0; i < fmnum; i++)
        {
            int tmp;
            cin >> tmp;
            fmweight[tmp] = true;
        }
        while (proone(fmweight))
        {
    
        }
        if (fmweight[1])
        {
            return true;
        }
        return false;
    }

    这种方法试了,数量相近是可以的,但是如果跨度太大,时间上就不行了。示例,输入3,然后输入9 8000 20000,计算的非常慢。

    怎么优化呢?思路应该变一下,引入丢番图方程,参考(https://blog.csdn.net/luyuncheng/article/details/8558162),并且感谢群里的小伙伴。

    定理2:如果a1,a2,…,an是正整数,那么方程a1*x1+a2*x2+…+an*xn=c有整数解,当且仅当d=(a1,a2,……,an)能整除c,另外当存在一个解的时候,那么方程有无穷多个解

    a1 a2表示砝码质量,x1 x2表示这种砝码需要几个,把c设置为1,就是我们需要解决的问题,n元一次方程是否有解。也就是求给定的n中砝码重量的最大公约数是1.

    bool test()
    {
        int fmnum = 0;
        cin >> fmnum;
        set<int> fmweight;
        int imax = 0;
        int imin = 0;
        for (size_t i = 0; i < fmnum; i++)
        {
            int tmp;
            cin >> tmp;
            imax = tmp;
            imin = tmp;
            fmweight.insert(tmp);
        }
        if (fmnum == 0)
        {
            return false;
        }
        if (fmnum == 1 && imax != 1)
        {
            return false;
        }
        for (auto& iter : fmweight)
        {
            imax = iter;
            if (imin > imax)
            {
                imin = imin ^ imax;
                imax = imin ^ imax;
                imin = imin ^ imax;
            }
            while (0 != imax % imin)
            {
                imax = imax / imin;
                if (imin > imax)
                {
                    imin = imin ^ imax;
                    imax = imin ^ imax;
                    imin = imin ^ imax;
                }
            }
            if (imin == 1)
            {
                return true;
            }
        }
    
        return false;
    }

     群里的小伙伴说反复求余也可以。具体没有证明。就是不断的把给出的砝码重量求余,然后再把余数加进来计算,知道求出1或是求不出1.

  • 相关阅读:
    wxpython自定义按钮
    wxPython修改文本框颜色
    strcat函数
    fopen作用
    Less 语法快速入门
    Echarts构建图表
    伪数组
    MVC,MVP,MVVM基本原理
    VUE常见指令
    Apply,Call,bind对比
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/12093032.html
Copyright © 2020-2023  润新知