砝码称重

题目

给定几种重量的砝码,数量不限,判断是否可以称出任意重量的物品。有时间空间限制,时间是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.

posted @ 2019-12-26 15:50  秋来叶黄  阅读(3023)  评论(0编辑  收藏  举报