Files

122 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [1681. Minimum Incompatibility](https://leetcode.com/problems/minimum-incompatibility/)
## 题目
You are given an integer array `nums` and an integer `k`. You are asked to distribute this array into `k` subsets of **equal size** such that there are no two equal elements in the same subset.
A subset's **incompatibility** is the difference between the maximum and minimum elements in that array.
Return *the **minimum possible sum of incompatibilities** of the* `k` *subsets after distributing the array optimally, or return* `-1` *if it is not possible.*
A subset is a group integers that appear in the array with no particular order.
**Example 1:**
```
Input: nums = [1,2,1,4], k = 2
Output: 4
Explanation: The optimal distribution of subsets is [1,2] and [1,4].
The incompatibility is (2-1) + (4-1) = 4.
Note that [1,1] and [2,4] would result in a smaller sum, but the first subset contains 2 equal elements.
```
**Example 2:**
```
Input: nums = [6,3,8,1,3,1,2,2], k = 4
Output: 6
Explanation: The optimal distribution of subsets is [1,2], [2,3], [6,8], and [1,3].
The incompatibility is (2-1) + (3-2) + (8-6) + (3-1) = 6.
```
**Example 3:**
```
Input: nums = [5,3,3,6,3,3], k = 3
Output: -1
Explanation: It is impossible to distribute nums into 3 subsets where no two elements are equal in the same subset.
```
**Constraints:**
- `1 <= k <= nums.length <= 16`
- `nums.length` is divisible by `k`
- `1 <= nums[i] <= nums.length`
## 题目大意
给你一个整数数组 nums 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中使得同一个子集里面没有两个相同的元素。一个子集的 不兼容性 是该子集里面最大值和最小值的差。
请你返回将数组分成 k 个子集后各子集 **不兼容性****和** 的 **最小值** ,如果无法分成分成 k 个子集返回 -1 。子集的定义是数组中一些数字的集合对数字顺序没有要求。
## 解题思路
- 读完题最直白的思路就是 DFS。做法类似第 77 题。这里就不赘述了。可以见第 77 题题解。
- 这一题还需要用到贪心的思想。每次取数都取最小的数。这样可以不会让最大数和最小数在一个集合中。由于每次取数都是取最小的,那么能保证不兼容性每次都尽量最小。于是在 order 数组中定义取数的顺序。然后再把数组从小到大排列。这样每次按照 order 顺序取数,都是取的最小值。
- 正常的 DFS 写完提交,耗时是很长的。大概是 1532ms。如何优化到极致呢这里需要加上 2 个剪枝条件。第一个剪枝条件比较简单,如果累计 sum 比之前存储的 res 大,那么直接 return不需要继续递归了。第二个剪枝条件就非常重要了可以一下子减少很多次递归。每次取数产生新的集合的时候要从第一个最小数开始取一旦取了后面就不需要再循环递归了。举个例子[1,2,3,4],第一个数如果取 2集合可以是 [[2,3],[1,4]] 或 [[2,4], [1,3]], 这个集合和[[1,3],[2,4]]、[[1,4], [2,3]] 情况一样。可以看到如果取出第一个最小值以后,后面的循环是不必要的了。所以在取下标为 0 的数的时候,递归到底层以后,返回就可以直接 break不用接下去的循环了接下去的循环和递归是不必要的。每组组内的顺序我们并不关心只要最大值和最小值在分组内即可。另外组间顺序我们也不关心。所以可以把排列问题 O(n!) 时间复杂度降低到组合问题 O(2^n)。加了这 2 个剪枝条件以后,耗时就变成了 0ms 了。beats 100%
## 代码
```go
package leetcode
import (
"math"
"sort"
)
func minimumIncompatibility(nums []int, k int) int {
sort.Ints(nums)
eachSize, counts := len(nums)/k, make([]int, len(nums)+1)
for i := range nums {
counts[nums[i]]++
if counts[nums[i]] > k {
return -1
}
}
orders := []int{}
for i := range counts {
orders = append(orders, i)
}
sort.Ints(orders)
res := math.MaxInt32
generatePermutation1681(nums, counts, orders, 0, 0, eachSize, &res, []int{})
if res == math.MaxInt32 {
return -1
}
return res
}
func generatePermutation1681(nums, counts, order []int, index, sum, eachSize int, res *int, current []int) {
if len(current) > 0 && len(current)%eachSize == 0 {
sum += current[len(current)-1] - current[len(current)-eachSize]
index = 0
}
if sum >= *res {
return
}
if len(current) == len(nums) {
if sum < *res {
*res = sum
}
return
}
for i := index; i < len(counts); i++ {
if counts[order[i]] == 0 {
continue
}
counts[order[i]]--
current = append(current, order[i])
generatePermutation1681(nums, counts, order, i+1, sum, eachSize, res, current)
current = current[:len(current)-1]
counts[order[i]]++
// 这里是关键的剪枝
if index == 0 {
break
}
}
}
```