找出Uneaten Leaves算法错误

时间:2016-02-02 08:14:16

标签: c++ algorithm discrete-mathematics

我在面试挑战中遇到了这个问题

  K毛毛虫正在通过每片毛虫的N片叶子吃东西   所有毛毛虫都以独特的顺序从一片叶子落到另一片叶子   在0号位置的树枝处,落在两片之间的叶子上   每个履带j具有相关的跳跃数Aj。一个   毛茸茸的跳跃数j吃叶子的位置   j的倍数。它将以j,2j,3j ......的顺序进行。直到它   到达叶子的末端,它停止并建立它的茧。特定   一组K元素,我们需要确定数字   未吃的叶子。

     

约束:

     

1 <= N <= 10 9

     

1 <= K <= 15

     

1&lt; = A [i]&lt; = 10 9

     

输入格式:

     <=> N =没有未吃的叶子。

     

K =毛毛虫的数量。

     

A =整数数组。   跳数输出:

     

整数nu。未吃的叶子

     

示例输入:

     

10
  3
  2
  4
  5

     

输出:

     

4

     

解释

     

[2,4,5]是3个成员的跳跃数。所有叶子都是2,4和5的倍数。只留下4根编号为1,3,7,9的叶子。

解决这个问题的天真方法是拥有一个包含所有N个数字的布尔数组,并迭代每个毛虫并记住它被吃掉的叶子。

int uneatenusingNaive(int N, vector<int> A)
{
    int eaten = 0;
    vector<bool>seen(N+1, false);
    for (int i = 0; i < A.size(); i++)
    {
        long Ai = A[i];
        long j = A[i];
        while (j <= N && j>0)
        {
            if (!seen[j])
            {
                seen[j] = true;
                eaten++;
            }
            j += Ai;
        }
    }
    return N - eaten;
}

这种方法通过了10个测试案例中的8个并且对2个案例给出了错误的答案
使用Inclusion Exclusion principle的另一种方法,可以找到herehere的解释 下面是我的第二种方法的代码

 int gcd(int a, int b)
    {
        if (b == 0)
            return a;
        return gcd(b, a%b);
    }
    int lcm(int i, int j)
    {
        return i*j / gcd(i, j);
    }

    vector<vector<int>> mixStr(vector<vector<int>> & mix, vector<int>& A, unordered_map<int, int> & maxStart)
    {
        vector<vector<int>> res;
        if (mix.size() == 0)
        {
            for (int i = 0; i < A.size(); i++)
            {
                vector<int> tmp;
                tmp.push_back(A[i]);
                res.push_back(tmp);
            }
            return res;
        }


        for (int i = 0; i<mix.size(); i++)
        {
            int currSlotSize = mix[i].size();
            int currSlotMax = mix[i][currSlotSize - 1];

            for (int j = maxStart[currSlotMax]; j < A.size(); j++)
            {
                vector<int> tmp(mix[i]);
                tmp.push_back(A[j]);
                res.push_back(tmp);
            }
        }
        return res;
    }
    int uneatenLeavs(int N, int k, vector<int> A)
    {
        int i = 0;
        vector<vector<int>> mix;
        bool sign = true;
        int res = N;
        sort(A.begin(), A.end());
        unordered_map<int,int> maxStart;
        for (int i = 0; i < A.size(); i++)
        {
            maxStart[A[i]] = i + 1;
        }
        int eaten = 0;


        while (mix.size() != 1)
        {   

            mix = mixStr(mix, A, maxStart);
            for (int j = 0; j < mix.size(); j++)
            {
                int _lcm = mix[j][0];
                for (int s = 1; s < mix[j].size(); s++)
                {
                    _lcm = lcm(mix[j][s], _lcm);
                }
                if (sign)
                {
                    res -= N / _lcm;
                }
                else
                {
                    res += N / _lcm;
                }
            }
            sign = !sign;
            i++;
        }
        return res;
    }

这种方法只通过了一个1/10的测试用例。并且对于其余的测试用例超出时间限制并且错误答案。

问题:
我在第一种或第二种方法中缺少的是100%正确。

1 个答案:

答案 0 :(得分:3)

使用包含 - 排除定理是正确的方法,但是,您的实现似乎太慢。我们可以使用bitmasking技术来获得 O(K * 2 ^ K)时间复杂度。

看看这个:

long result = 0;

for(int i = 1; i < 1 << K; i++){
     long lcm = 1;
     for(int j = 0; j < K; j++)
        if(((1<<j) & i) != 0) //if bit j is set, compute new LCM after including A[j]
           lcm *= A[j]/gcd(lcm, A[j]);
     if(number of bit set in i is odd)
        result += N/lcm;
     else
        result -= N/lcm; 
}

对于您的第一种方法, O(N * K)时间复杂度算法,N = 10 ^ 9且K = 15,它将太慢,并且可能导致内存限制超过/时限超过。

请注意,lcm可能大于N,因此需要进行额外检查。