c++ - 找出 Uneaten Leaves 算法错误

标签 c++ algorithm discrete-mathematics

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

K caterpillars are eating their way through N leaves, each caterpillar falls from leaf to leaf in a unique sequence, all caterpillars start at a twig at position 0 and falls onto the leaves at position between 1 and N. Each caterpillar j has an associated jump number Aj. A caterpillar with jump number j eats leaves at positions that are multiple of j. It will proceed in the order j, 2j, 3j…. till it reaches the end of the leaves and it stops and build its cocoon. Given a set A of K elements , we need to determine the number of uneaten leaves.

Constraints:

1 <= N <= 109

1 <= K <= 15

1 <= A[i] <= 109

Input format:

N = No of uneaten leaves.

K = No. of caterpillars.

A = Array of integer. jump numbers Output:

The integer nu. Of uneaten leaves

Sample Input:

10
3
2
4
5

Output:

4

Explanation:

[2, 4, 5] is the 3-member set of jump numbers. All leaves which are multiple of 2, 4, and 5 are eaten. Only 4 leaves which are numbered 1,3,7,9 are left.

解决这个问题的简单方法是有一个包含所有 N 个数字的 bool 数组,并遍历每只毛毛虫并记住被它吃掉的叶子。

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% 正确的。

最佳答案

使用包含-排除定理是正确的方法,但是,您的实现似乎太慢了。我们可以使用位掩码技术来获得 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,因此需要额外检查。

关于c++ - 找出 Uneaten Leaves 算法错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35148149/

相关文章:

c++ - ASIO 库 - 未调用处理程序

android - Cocos2d-x C++开发工具(无需完整的IDE)

c++ - 为什么我可以用指针分配结构?

math - "Law of the Eight"是什么?

lisp - 类似于图形但具有不同类型边的数学对象的名称是什么?

python - 创建集合的分区,使得每个分区的长度相同

c++ - 带有 std::thread 的 MVSE12 中的错误 C2248

python - Python 中的数组初始化

algorithm - 合并排序最坏情况下的字典排序运行时间?

algorithm - 如何检测(心电图)波中的模式?