leetcode1681
/*状态压缩 这种集合元素不是特别多(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]; } };