砝碼稱重


題目

給定幾種重量的砝碼,數量不限,判斷是否可以稱出任意重量的物品。有時間空間限制,時間是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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM