/*状态压缩 这种集合元素不是特别多(n<16)的集合等分,可以考虑使用基于二进制的集合状态记录
首先预处理所有含有n/k元素的value值 用于最优化(动态规划)的时候使用
然后dp过程其实就是暴搜 针对子集合状态mask 考察他所有的子集合sub 有如下状态转移方程
dp[mask] = min{sub合法 |dp[mask ^ sub] + value[sub]}*/
class Solution {
public:
int minimumIncompatibility(vector<int>& nums, int k) {
int n = nums.size();
vector <int> value(1 << n , -1);
vector <int> hash(n + 1);
for(int sub = 0;sub < (1 << n);sub++){
if(__builtin_popcount(sub) == n / k){//含有n/k元素的子集合的状态
for(int j = 0;j < n;j++){//该状态是否合法 [查重]
if(sub & (1 << j)){
hash[nums[j]]++;
}
}
int flag = 1;
for(int j = 1;j < n + 1;j++){
if(hash[j] > 1) {
flag = 0;
break;
}
}
if(flag == 1){
int Min = INT_MAX;
int Max = INT_MIN;
for(int i = 1;i <= n;i++)
if(hash[i] > 0){
Min = min(Min , i);
Max = max(Max , i);
}
value[sub] = Max - Min;
}
for(int j = 0;j < n;j++){//该状态是否合法 [查重]
if(sub & (1 << j)){
hash[nums[j]]--;
}
}
}
}
vector<int> dp(1 << n , -1);
dp[0] = 0;
for(int mask = 0;mask < (1<<n);mask++){
if(__builtin_popcount(mask) %(n/k) == 0){
for(int sub = mask;sub;sub = (sub - 1)&mask){//枚举当前状态的子集合
if(value[sub] != -1 && dp[mask ^ sub] != -1){
//子集合合法(含有规定元素 即提前预处理过) 剩余集合非空(合法)
if(dp[mask] == -1){
dp[mask] = dp[mask ^ sub] + value[sub];
}
else{
dp[mask] = min(dp[mask] , dp[mask^sub] + value[sub]);
}
}
}
}
}
return dp[(1 << n) - 1];
}
};