【数学】C025_卡牌分组(暴力枚举 | 最大公约数)

一、题目描述

In a deck of cards, each card has an integer written on it.
Return true if and only if you can choose X >= 2 such that 
it is possible to split the entire deck into 1 or more groups of cards, where:

  - Each group has exactly X cards.
  - All the cards in each group have the same integer.
 
Example 1:
Input: [1,2,3,4,4,3,2,1]
Output: true
Explanation: Possible partition [1,1],[2,2],[3,3],[4,4]

Example 2:
Input: [1,1,1,2,2,2,3,3]
Output: false
Explanation: No possible partition.

Input: [1,1]
Output: true
Possible partition: [1,1]

Input: [1,1,2,2,2,2]
Output: true
Explanation: Possible partition  [1,1][2,2][2,2]

Note:
   1 <= deck.length <= 10000 (牌数的上下限)
   0 <= deck[i] < 10000 (牌的点数的上限)

二、题解

(1) 暴力枚举

枚举所有可能可能符合条件的 X(即每个组合的牌数), 根据题意得: N%X==0(N为卡牌数),假设有 C_i 张写有数字i的牌,每组数字为 i 的卡则有 X 张,那么一定会有 C_i % X == 0

/**
 * 执行用时 3 ms 击败了 78.22% 的java用户
 * 内存消耗 39.2MB 击败了 91% 的java用户
 * @param deck
 * @return
 */
public boolean hasGroupsSizeX(int[] deck) {
  int[] count = new int[10000]; // 因为牌的点数最大为10000,所有要记录该点出现的次数就需要最大下标10000
  for(int n : deck) {
    count[n]++;
  }
  int N = deck.length;
  LinkedList<Integer> valList = new LinkedList<>();
  for(int n : count) {
    if(n > 0) valList.addLast(n);
  }

  outFor: for (int X = 2; X <= N; X++) {
            if(N % X == 0) {
              for (int val : valList) {
                if(val % X != 0)
                  continue outFor;
              }
              return true;
            }
          }
  return false;
}

复杂度分析

  • 时间复杂度:O(N^2 log logN),其中 NN 是卡片个数。本文不证明找到所有 NN 的因数个数的上界是 O(N log log N)。

  • 空间复杂度:O(N)。

(2) 最大公约数

/**
 * 最大公约数,假设 数字 i 有C_i张牌,其中没堆卡牌有 X 张,那么 X 一定可以整除 C_i,
 * 求出count[i]的最大公约数是否 >= 2就可以得知,是否可按条件分组。
 * 比如deck == {1,1,1,2,2,2,3,3},最大公约数是1。不满足条件
 * deck{1,1,2,2,3,3}的gcd==2,则满足
 *
 * 执行用时 2 ms 击败了 99.56 % 的java用户
 * 内存消耗 38.8MB 击败了 91.2% 的java用户
 *  @param deck
 * @return
 */
public boolean hasGroupsSizeX2(int[] deck) {

  int[] count = new int[10000];
  for(int n : deck)
    count[n]++;

  int g = -1;
  for (int i = 0; i < count.length; i++) {
    if(count[i] > 0) {
      if(g == -1)
        g = count[i];
      else
        g = gcd(g, count[i]);
    }
  }
  return g >= 2;
}
private int gcd(int x, int y) {
  return x == 0 ? y : gcd(y%x, x);
}

复杂度分析

  • 时间复杂度:O(N log^2 N)
    N),其中 N 是卡片的个数。假设数字 i 有 Ci 张,那么每次 gcd 操作的复杂度是 O(log^2 C_i),存在更优的上界,但不在本文的考虑范围内。
  • 空间复杂度:O(N)
发布了300 篇原创文章 · 获赞 48 · 访问量 8067

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/103996177