Minimum Incompatibility 最小不兼容性
问题描述:
给你一个整数数组 nums 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中,使得同一个子集里面没有两个相同的元素。
一个子集的 不兼容性 是该子集里面最大值和最小值的差。
请你返回将数组分成 k 个子集后,各子集 不兼容性 的 和 的 最小值 ,如果无法分成分成 k 个子集,返回 -1 。
子集的定义是数组中一些数字的集合,对数字顺序没有要求。
k , n u m s . l e n g t h 范围 [ 1 , 16 ] , n u m s . l e n g t h m o d k = = 0 , n u m s [ i ] 范围 [ 1 , n u m s . l e n g t h ] k ,nums.length 范围[1,16 ] ,nums.length\mod k==0 ,nums[i] 范围[1,nums.length ] k,nums.length范围[1,16],nums.lengthmodk==0,nums[i]范围[1,nums.length]
分析
< E m p t y M a t h B l o c k > <Empty \space Math \space Block> <Empty Math Block>
问题要求把整数数组 n u m s nums nums的 n n n个元素平均分成 k k k个组,而每个组就会有 n / k n/k n/k个元素。
与此同时,根据题目,每个组会有一个 i n c o m p a t i b i l i t y incompatibility incompatibility值,把 k k k个组的 i n c o m p a t i b i l i t y incompatibility incompatibility值累加得到 s u m sum sum,要求找到最小的 s u m sum sum。问题大概就是这么个意思。
i n c o m p a t i b i l i t y incompatibility incompatibility的值是由这一个组的 m a x − m i n max-min max−min产生的。同时有限制不允许一个组有重复的元素。
所以理论上 i n c o m p a t i b i l i t y > 0 incompatibility>0 incompatibility>0。
首先很容易根据条件判断出哪种情况是不可能得到sum的。即 n u m s nums nums中的某个元素次数 c n t [ i ] > k cnt[i]>k cnt[i]>k,这样就无法满足组内元素不重复。
贪心策略?
到此就可以开始找了,朴素的想法,既然要最小,那么就让每个组最小这样就可以得到整体的最小。
那么第一步就是先排序,这样相邻的元素就会靠近,把相邻的满足条件的元素划分到一个组中,这样就可以得到最小了。
想法很好,但是并不能保证最终的结果是最小的。
也就是说简单的贪心策略并不能完全保证该策略的最终结果是最小的。
剩下的就只有暴力了, m = n / k m=n/k m=n/k, m m m为子集的大小,n最大可取 16 16 16,所以 n n n的所有非空子集数量就是 2 16 − 1 2^{16}-1 216−1 ,即65535。
所以通过条件筛选出所有符合题意的子集。
- 满足 子集大小size==m,
- 满足 子集中不能有重复数字元素。
同时还可以将子集的 i n c o m p a t i b i l i t y incompatibility incompatibility值计算出来。
到此每个子集都是符合条件的,剩下的就是把从X个子集中选出k个,并且他们的并集== 2 16 − 1 2^{16}-1 216−1。
d f s ( s t a t e ) = min s u b ∈ U ∖ s t a t e d f s ( s t a t e ⊕ s u b ) + m a p [ s u b ] dfs(state) = \min_{sub\in {U\setminus state} } {dfs(state\oplus sub)+map[sub]} \\ dfs(state)=sub∈U∖statemindfs(state⊕sub)+map[sub]
代码
class Solution {int[] memo,map;int INF = 1<<30,u=0;public int minimumIncompatibility(int[] nums, int k) {int n = nums.length,max = 0;int[] cnt = new int[n+1];for(int num: nums){cnt[num]++;if(max<cnt[num]) max=cnt[num];if(max>k) return -1;}Arrays.sort(nums);memo = new int[1<<n];int m = n/k,state = 1;u = (1<<n)-1;map = new int[1<<n];for(;state<=u;state++){map[state] = memo[state] = -1;if(Integer.bitCount(state)!=m) continue; int curmin=16,curmax = 0,pre=-1;boolean dup =false;for(int i = 0;i<n;i++){if((state&(1<<i))==0) continue;if(pre==nums[i]){dup = true;break;}pre = nums[i];curmin= Math.min(curmin,nums[i]);curmax= Math.max(curmax,nums[i]);}if(dup) continue;map[state] = curmax-curmin;}return dfs(u);}public int dfs(int state){ if(state==0) return 0;if(memo[state]!=-1) return memo[state];int res = INF;for(int sub =state;sub>0;sub =(sub-1)&state){if(map[sub]==-1) continue; int nx = state^sub; int t = dfs(nx);res = Math.min(res,map[sub]+t);} return memo[state]=res;}
}
时间复杂度 O ( 3 n ) O( 3^ n) O(3n)
空间复杂度 O ( 2 n ) O(2^n) O(2n)
Tag
Memoization
Bit Manipulation
Dynamic Programming
Bitmask