如果一个N x M的乘法表按顺序排列,中间的数字是什么?

6
如果我有一个乘法表,比如3x5大小的:
1  2  3  4  5
2  4  6  8 10
3  6  9 12 15

然后我按顺序排列了这些数字:

1 2 2 3 3 4 4 5 6 6 8 9 10 12 15

中间的数字是多少?在这个例子中,是5。

NM总是奇数,因此只有一个答案。

是否有快速解决方案?我正在寻找类似于O(N log NM)的东西。

这是某种作业,但我真的弄丢了。我想到了一些想法,但它们都有一些缺点:

public class Table {

    public static void main(String[] ar) {
        Scanner scanner = new Scanner(System.in);
        int w = scanner.nextInt();
        int h = scanner.nextInt();

        int[] s = new int[w * h + 1];
        for (int i = 1; i <= w; i++)
            for (int j = 1; j <= h; j++)
                s[i * j] = s[i * j] + 1;

        int sum = 0;
        for (int i = 0; i < s.length; i++) {
            sum += s[i];
            if (sum >= s.length / 2) {
                System.out.println(i);
                break;
            }
        }
    }

}

这个解决方案可以快速解决大部分测试 (<4s),但对于大的 N 和 M,会超出内存限制 (我不知道确切的限制)。

这个想法是跟踪每个数字的出现次数,然后按顺序迭代所有数字,在每次迭代中添加出现次数。当出现次数大于或等于 w * h / 2 时,它就是中间的数字,我们将其打印出来。

public class Table {

    public static void main(String[] ar) {
        Scanner scanner = new Scanner(System.in);
        int w = scanner.nextInt();
        int h = scanner.nextInt();

        int sum = 0;
        for (int i = 1; i <= w * h; i++) {
            for (int j = 1; j <= Math.sqrt(i); j++) {
                if (i % j == 0) {
                    int k = i / j;
                    if (k <= w && k != j) sum++;
                    if (k <= h && k != j) sum++;
                    if (k <= w && k <= h && k == j) sum++;
                }                
            }

            if (sum >= (w * h + 1) / 2) {
                System.out.println(i);
                break;
            }
        }
    }

}

尝试克服内存限制,我尝试按顺序计算每个数字的出现次数,直到中间位置。我注意到,每个数字在乘法表中出现的次数等于它们拥有的因子数量。
速度还不够快。
有人可以提供任何指针吗?我知道在建议的 O(N log NM) 解决方案中使用了二分查找。
1 <= N <= 10^5
1 <= M <= 10^5

解决方案!

好的,感谢@PeterdeRivaz的帮助,我成功地找到并实现了解决我的问题的方法。这个想法就像他所描述的那样,下面是实际的实现:

    public class Kertotaulu {

public static void main(String[] ar) { Scanner scanner = new Scanner(System.in); long h = scanner.nextLong(); long w = scanner.nextLong();
long min = 1; long max = w*h; long mid = 0; while (min <= max) { mid = (min + max) / 2; long sum = 0; for (int i = 1; i <= h; i++) sum += Math.min(mid / i, w); sum--;
if (sum < (w * h) / 2) min = mid + 1; else if (sum > (w * h) / 2) max = mid - 1; else break; }
long sum = 0; for (int i = 1; i <= h; i++) sum += Math.min((mid - 1) / i, w); sum--;
if (sum == (w * h) / 2) System.out.println(mid - 1); else System.out.println(mid); }
}


基于你的第二种方法,你能找出每个数字的质因数分解,并用它来计算它在表格中出现的次数吗?(注意:这只是我脑海中的想法;我还没有尝试过。) - ajb
@ajb 你是什么意思?例如,在一个6x6的表格中,数字6出现了4次(1x6,2x3,3x2,6x1),但它的质因数只有2和3。 - Olavi Mustanoja
现在这已经不重要了,因为我认为发布的答案是正确的。我的意思是,如果N的质因数分解是(p1 ^ n1)(p2 ^ n2) ... (pm ^ nm),则N的约数数量为(1 + n1)(1 + n2)...(1 + nm)。因此,在6 = 2 ^ 1 * 3 ^ 1的情况下,约数的数量为(1 + 1)(1 + 1)= 4。 - ajb
1个回答

2
您可以通过计算乘法表中小于该值的条目数来使用二分查找该值。
这将需要在二分搜索中进行log(NM)次迭代,因此我们需要能够在O(N)时间内计算出计数,总复杂度为O(Nlog(NM))。
这可以通过依次考虑每个乘法表来完成。例如,假设我们的测试值为8,我们正在考虑3倍表。
小于八的值将是3*1和3*2。我们可以通过将测试值8除以表3并向下取整来找到有多少个值,即floor(8/3) = 2,因此3倍表将给我们一个计数为2。

谢谢,这看起来非常不错。我回家后会试一下 :) - Olavi Mustanoja
“the values less than ten...” 你可能是想说“小于八”的值吧? - Olavi Mustanoja
在问题中添加了一个解决方案。 - Olavi Mustanoja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接