【问题标题】:ADACON : Ada and connections SPOJ(TLE)ADACON : Ada 和连接 SPOJ(TLE)
【发布时间】:2017-12-18 12:24:57
【问题描述】:

This 是 SPOJ 的问题。我得到 TLE。需要帮助来提高它的时间复杂度。我知道有一个测试用例会失败。但我会在时间复杂度降低后处理它。

瓢虫艾达和她的朋友一起旅行。他们每人在那里买了一件纪念品。由于他们都是数学家,所以每个人都买了一个数字。他们想修改数字以在彼此之间建立某种联系。他们决定修改他们的 GCD 大于 1 的数字(gcd(a1,a2,a3,...,aN) > 1)。无论如何,改变一个数字并不容易 - 他们唯一能做的就是去找一位数学教授,他可以将数字 A 变成数字 A+1A-1 。由于这种操作并不便宜,他们希望尽量减少这种操作的数量。一个数字可以伪造任意多次。

注意:gcd(a,0)==a(所以两个 0 的 gcd 也是 0)

输入
第一行包含一个整数1 ≤ N ≤ 3*10^5,即在旅途中的朋友的数量(以及数字的数量)。

第二行包含N个整数0 ≤ a_i ≤ 10^6

输出
用最少的操作打印一行以在所有数字之间建立连接。

输入示例
5
3 9 7 6 31
示例输出
2
示例输入 2
9
3 4 5 7 8 9 11 12 13
示例输出 2
6
示例输入 3
5
7 7 11 17 1
示例输出 3
5

方法

首先我在给定的数字数组(using function findPrimes()) 中找到直到(largest/2 + 1) 元素的素数。然后对于数组中的每个元素,找出每个素数需要多少个operations 作为其除数。每个素数的最小总和,我正在打印作为解决方案。

代码

import java.io.*;


public class Main
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int largest = Integer.MIN_VALUE;
        int n = Integer.parseInt(br.readLine());
        int[] arr = new int[n];
        String[] strArr = br.readLine().split(" ");
        for(int i = 0 ; i < n ; i++)
        {
            arr[i] = Integer.parseInt(strArr[i]);
            if(arr[i] > largest)
            {
                largest = arr[i];
            }
        }
        func(n,arr,largest);

    }

    public static void func(int n,int[] arr,int largest)
    {
        int[] primes = findPrimes(largest / 2 + 1);
        //int[] primes = findPrimes((int)Math.sqrt(largest));
        int lenOfPrimes = primes.length;
        int[] mat = new int[lenOfPrimes];
        for(int j = 0 ; j < lenOfPrimes ; j++)
        {
                if(arr[0] < primes[j])
                {
                    mat[j] = primes[j] - arr[0];
                }
                else if(arr[0] % primes[j] == 0)
                {
                    mat[j] = 0;
                }
                else
                {
                    int rem = arr[0] % primes[j];
                    mat[j] = Math.min(rem,primes[j] - rem);
                }

        }
        for(int i = 1 ; i < n ; i++)
        {
            for(int j = 0 ; j < lenOfPrimes ; j++)
            {
                if(arr[i] < primes[j])
                {
                    mat[j] = mat[j] + primes[j] - arr[i];
                }
                else if(arr[i] % primes[j] == 0)
                {
                    mat[j] = mat[j] + 0;
                }
                else
                {
                    int rem = arr[i] % primes[j];
                    mat[j] += Math.min(rem,primes[j] - rem);
                }
            }
        }

        int smallest = Integer.MAX_VALUE;
        for(int i = 0 ; i < lenOfPrimes ;i++)
        {
            if(mat[i] < smallest)
                smallest = mat[i];
        }

        System.out.println(smallest);
    }

    public static int[] findPrimes(int upto)
    {
        boolean[] primes = new boolean[upto + 1];
        for(int i = 0 ; i < upto + 1 ; i++)
            primes[i] = true;

        int count = 0;
        primes[0] = primes[1] = false;

        int limit = (int)Math.sqrt(upto + 1);
        for(int i = 2 ; i < upto + 1; i++)
        {
            if(primes[i] == true)
            {
                count++;
                if(i <= limit)
                {
                    for(int j = i * i ; j < upto + 1 ; j += 2 * i)
                    {
                        primes[j] = false;
                    }
                }
            }
        }

        int[] primeContainer = new int[count];
        int index = 0;
        for(int i = 2 ; i < upto + 1 ; i++)
        {
            if(primes[i] == true)
            {
                primeContainer[index++] = i;
                if(index == count)
                    break;
            }
        }
        return primeContainer;
    }
}

【问题讨论】:

  • 你应该解释你的代码背后的逻辑和思考。
  • 这里有一个提示:看看 a_i mod 2。现在,要使它们都被 2 整除需要多少次操作?现在看看 a_i mod 3,...
  • 我已经添加了我的方法。你是想传达相同的还是不同的东西?
  • 更多Code Review
  • @JoopEggen 绝对不是:“我知道有一个测试用例会失败。”听起来代码没有按预期工作,因此不适合代码审查。

标签: java algorithm math number-theory


【解决方案1】:

您正在尝试的解决方案会给您正确的答案。但是由于在 1000000 之前有很多素数,(~ 78000)因此 78000*300000 肯定会给你 TLE。
试着用筛子来思考。埃拉托色尼筛的工作时间为 O(nlogn)。

现在您可能已经知道您将更改数字,使其可以被某个素数整除。 (在您的算法中,您只考虑素数)。所以现在让我们取一个质数,比如 7。现在你需要找到从 0 到 3 的数字转换的数量,因为你需要将这些数字更改为 0。同样,你需要找到从 4 到 10 的数字的数量。考虑到最小操作,将它们更改为 7 以使它们可被 7 整除。同样,您可以对从 11 到 17 的数字执行相同的操作,将它们更改为 14,以此类推,直到 1000000 为止。您需要对所有素数执行相同的操作。这可以使用筛子来实现。

这种情况下的操作数将是 n/2 + n/3 + n/5 + n/7 + n/11 + .... ~ nlogn。

您可以从这里阅读更多关于筛子的信息:https://www.geeksforgeeks.org/sieve-of-eratosthenes/

【讨论】:

  • 这种方法应该如何解决问题?你能详细说明一下吗?
【解决方案2】:

可能为时已晚,但让我回答这个问题。 我能够使用以下方法解决这个问题。 对于所有 15 个测试用例,我的 Java 解决方案在 SPOJ 上花费 3.8 S。 1.在 O(log n) 中找到 n 的素数除数 来源https://www.geeksforgeeks.org/prime-factorization-using-sieve-olog-n-multiple-queries/ 2. 在计算因式分解存储素数除数时,可以说 UniquePrimeWhicheDividsAtleastOneNumber[] 如果此 UniquePrimeWhicheDividsAtleastOneNumber 不可用,请始终在此保留 2。 UniquePrimeWhicheDividsAtleastOneNumber[0]=2 3. 现在你可以遍历这些素数,并找到这些素数的最小提醒之和。

        long minTemp = 0, minAns = Long.MAX_VALUE;
        for (int i = 0; i < UniquePrimeWhicheDividsAtleastOneNumber.length; i++) {
            for (int j = 0; j < n; j++) {
                int rem = InputNumbers[j] % UniquePrimeWhicheDividsAtleastOneNumber[i];
                minTemp += Math.min(rem, UniquePrimeWhicheDividsAtleastOneNumber[i] - rem);
                if (minTemp > minAns)
                    break;// no need to compute sum of reminders if it exceeded the current minimum.
            }
            minAns = Math.min(minAns, minTemp);
            minTemp = 0;
        }
        
        minAns --> is your answer.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多