【Lintcode】1854. Partition Array III(配数学证明)

题目地址:

https://www.lintcode.com/problem/partition-array-iii/description

给定一个数组(可能有相同数字),再给定一个数字 k k k,问是否可以将其分成若干个不相交子集,每个子集的size都是 k k k,并且使得每个子集里的数字各不相同。

先证明,一个长度为 n n n的数组可以按上述规则划分,当且仅当以下两个条件同时成立:
1、 k ∣ n k|n kn
2、数组中没有相同的数字重复多于 n k \frac{n}{k} kn次。

证明:
先证必要性:第一点显然。第二点,若有数字出现了多于 n k \frac{n}{k} kn次,那么由鸽笼原理,必然有一个子集有重复元素,矛盾;

再证充分性:我们将数组按如下分类:
a 11 , a 12 , a 13 , . . . , a 1 , s 1 a_{11},a_{12},a_{13},...,a_{1,s_1} a11,a12,a13,...,a1,s1
a 21 , a 22 , a 23 , . . . , a 2 , s 2 a_{21},a_{22},a_{23},...,a_{2,s_2} a21,a22,a23,...,a2,s2

a j 1 , a j 2 , a j 3 , . . . , a j , s j a_{j1},a_{j2},a_{j3},...,a_{j,s_j} aj1,aj2,aj3,...,aj,sj
其中 a i 1 = a i 2 = . . . = a i , s i a_{i1}=a_{i2}=...=a_{i,s_i} ai1=ai2=...=ai,si,并且 a 11 , a 21 , . . . a_{11},a_{21},... a11,a21,...彼此不同(其实就是把相同数排在同一行里,不同行数不同)。不妨设 s 1 ≥ s 2 ≥ . . . ≥ s j s_1\ge s_2\ge ... \ge s_j s1s2...sj。由条件知道, ∑ i = 1 j s i = n \sum_{i=1}^j s_i=n i=1jsi=n,并且 s 1 ≤ n k , s 2 ≤ n k , . . . , s j ≤ n k s_1\le \frac{n}{k},s_2\le \frac{n}{k},...,s_j\le \frac{n}{k} s1kn,s2kn,...,sjkn,所以 j ≥ k j\ge k jk,并且如果 j > k j>k j>k,则必然有 s k + 1 < n k s_{k+1}< \frac{n}{k} sk+1<kn(否则的话就有 s 1 = . . . = s k = s k + 1 = n k s_1=...=s_k=s_{k+1}=\frac{n}{k} s1=...=sk=sk+1=kn,则有 s 1 + . . . + s k + s k + 1 > n s_1+...+s_k+s_{k+1}>n s1+...+sk+sk+1>n矛盾)。
接下来用数学归纳法。对于固定的 k k k,我们证明对于任意的 n = k t n=kt n=kt,其中 t = 1 , 2 , . . . t=1,2,... t=1,2,...,都存在符合条件的分割方式。如果 t = 1 t=1 t=1,那么每个数字只能出现一次,显然存在。假设 t = x t=x t=x的时候也存在,当 t = x + 1 t=x+1 t=x+1的时候,由于 j ≥ k j\ge k jk,所以我们可以将第一列里从上向下取 k k k个数,形成一组,显然这组没有重复元素。此时,还剩下 n − k n-k nk个元素,并且满足 k ∣ n − k k|n-k knk并且每个数字重复次数都不超过 n k − 1 \frac{n}{k}-1 kn1(这是因为 s k + 1 < n k s_{k+1}<\frac{n}{k} sk+1<kn,而 s k + 1 ≥ s k + 1 ≥ . . . ≥ s j s_{k+1}\ge s_{k+1}\ge ...\ge s_j sk+1sk+1...sj),由归纳假设,剩余数存在符合条件的分割,证明完毕。

代码如下:

import java.util.HashMap;
import java.util.Map;

public class Solution {
    /**
     * @param array: the input array
     * @param k: the sequence length
     * @return: if it is possible, return true, otherwise false
     */
    public boolean partitionArratIII(int[] array, int k) {
        // write your code here
        if (array.length % k != 0) {
            return false;
        }
        
        Map<Integer, Integer> count = new HashMap<>();
        for (int num : array) {
            count.put(num, count.getOrDefault(num, 0) + 1);
            if (count.get(num) > array.length / k) {
                return false;
            }
        }
        
        return true;
    }
}

时空复杂度 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值