算法。如何在数组中找到整数的最长子序列,使得序列中任意两个连续数的gcd大于1?

时间:2016-12-25 05:59:38

标签: algorithm optimization

给出一个整数数组。我们必须找到整数最长子序列的长度,这样序列中任何两个连续元素的gcd都大于1.

例如:如果array = [12,8,2,3,6,9]

那么一个这样的子序列可以是= {12,8,2,6,9} 另一个可以是= {12,3,6,9}

我试图通过动态编程来解决这个问题。假设maxCount是数组,使得maxCount [i]将具有这种最长子序列的长度 以索引i结束。

`maxCount[0]=1 ;

for(i=1; i<N; i++)
{

   max = 1 ;

   for(j=i-1; j>=0; j--)
   {   

      if(gcd(arr[i], arr[j]) > 1)
      {
      temp = maxCount[j] + 1 ;

      if(temp > max)
       max = temp ;
     }
    }

maxCount[i]=max;

}``

max = 0;

for(i=0; i<N; i++)
{
 if(maxCount[i] > max)
   max = maxCount[i] ;
}

cout<<max<<endl ;

`

但是,这种方法正在超时。由于其时间复杂度为O(N ^ 2)。我们可以改善时间复杂度吗?

2 个答案:

答案 0 :(得分:2)

条件&#34; gcd大于1&#34; 表示数字至少有一个公约数。因此,让dp[i]等于i可整除的数字上最长序列的长度。

int n;
cin >> n;

const int MAX_NUM = 100 * 1000;
static int dp[MAX_NUM];

for(int i = 0; i < n; ++i)
{
    int x;
    cin >> x;

    int cur = 1;
    vector<int> d;
    for(int i = 2; i * i <= x; ++i)
    {
        if(x % i == 0)
        {
            cur = max(cur, dp[i] + 1);
            cur = max(cur, dp[x / i] + 1);
            d.push_back(i);
            d.push_back(x / i);
        }
    }
    if(x > 1)
    {
        cur = max(cur, dp[x] + 1);
        d.push_back(x);
    }

    for(int j : d)
    {
        dp[j] = cur;
    }
}

cout << *max_element(dp, dp + MAX_NUM) << endl;

此解决方案的复杂度为O(N * sqrt(MAX_NUM))。实际上,您只能为素数计算dp值。要实现这一点,您应该能够在不到O(N^0.5)时间内(例如this method)获得素数因子分解。该优化应该将复杂性转化为O(N * factorization + Nlog(N))。作为内存优化,您可以将dp数组替换为mapunordered_map

答案 1 :(得分:0)

GCD 花费 log m 时间,其中 m 是数组中的最大数。因此,使用 Segment Tree 和二分搜索,可以将时间复杂度降低到 O(n log (m² * n))(使用 O(n log m) 预处理)。 This list 详细介绍了可用于 RMQ 类型查询并进一步降低复杂性的其他数据结构。

这是一种可能的实现方式:

#include <bits/stdc++.h>
using namespace std;

struct SegTree {
    using ftype = function<int(int, int)>;
    vector<int> vec;
    int l, og, dummy;
    ftype f;
    template<typename T> SegTree(const vector<T> &v, const T &x, const ftype &func) : og(v.size()), f(func), l(1), dummy(x) {
        assert(og >= 1);
        while (l < og) l *= 2;
        vec = vector<int>(l*2);
        for (int i = l; i < l+og; i++) vec[i] = v[i-l];
        for (int i = l+og; i < 2*l; i++) vec[i] = dummy;
        for (int i = l-1; i >= 1; i--) {
            if (vec[2*i] == dummy && vec[2*i+1] == dummy) vec[i] = dummy;
            else if (vec[2*i] == dummy) vec[i] = vec[2*i+1];
            else if (vec[2*i+1] == dummy) vec[i] = vec[2*i];
            else vec[i] = f(vec[2*i], vec[2*i+1]);
        }
    }
    SegTree() {}
    void valid(int x) {assert(x >= 0 && x < og);}
    int get(int a, int b) {
        valid(a); valid(b); assert(b >= a);
        a += l; b += l;
        int s = vec[a];
        a++;
        while (a <= b) {
            if (a % 2 == 1) {
                if (vec[a] != dummy) s = f(s, vec[a]);
                a++;
            }
            if (b % 2 == 0) {
                if (vec[b] != dummy) s = f(s, vec[b]);
                b--;
            }
            a /= 2; b /= 2;
        }
        return s;
    }
    void add(int x, int c) {
        valid(x);
        x += l;
        vec[x] += c;
        for (x /= 2; x >= 1; x /= 2) {
            if (vec[2*x] == dummy && vec[2*x+1] == dummy) vec[x] = dummy;
            else if (vec[2*x] == dummy) vec[x] = vec[2*x+1];
            else if (vec[2*x+1] == dummy) vec[x] = vec[2*x];
            else vec[x] = f(vec[2*x], vec[2*x+1]);
        }
    }
    void update(int x, int c) {add(x, c-vec[x+l]);}
};
// Constructor (where val is something that an element in the array is
// guaranteed to never reach):
// SegTree st(vec, val, func);

// finds longest subsequence where GCD is greater than 1
int longest(const vector<int> &vec) {
    int l = vec.size();
    SegTree st(vec, -1, [](int a, int b){return __gcd(a, b);});
    
    // checks if a certain length is valid in O(n log (m² * n)) time
    auto valid = [&](int n) -> bool {
        for (int i = 0; i <= l-n; i++) {
            if (st.get(i, i+n-1) != 1) {
                return true;
            }
        }
        return false;
    };
    
    int length = 0;
    // do a "binary search" on the best possible length
    for (int i = l; i >= 1; i /= 2) {
        while (length+i <= l && valid(length+i)) {
            length += i;
        }
    }
    return length;
}