题目地址:
https://www.lintcode.com/problem/partition-array-iii/description
给定一个数组(可能有相同数字),再给定一个数字 k k k,问是否可以将其分成若干个不相交子集,每个子集的size都是 k k k,并且使得每个子集里的数字各不相同。
先证明,一个长度为
n
n
n的数组可以按上述规则划分,当且仅当以下两个条件同时成立:
1、
k
∣
n
k|n
k∣n;
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
s1≥s2≥...≥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}
s1≤kn,s2≤kn,...,sj≤kn,所以
j
≥
k
j\ge k
j≥k,并且如果
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
j≥k,所以我们可以将第一列里从上向下取
k
k
k个数,形成一组,显然这组没有重复元素。此时,还剩下
n
−
k
n-k
n−k个元素,并且满足
k
∣
n
−
k
k|n-k
k∣n−k并且每个数字重复次数都不超过
n
k
−
1
\frac{n}{k}-1
kn−1(这是因为
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+1≥sk+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)。