1681. Minimum Incompatibility

问题:

给定一组数字,将其等分成k个个数相同的子数组。

求划分后,得到所有子数组最小差分和。

差分:子数组中,最大值-最小值

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

  

解法:

解法一:Backtracking(回溯算法)->TLE

  • 状态:到目前为止,选择后的结果path,以及选择后剩下元素的个数count
  • 选择:任意4个不同的元素(组合)(count(元素i)!=0)
  • 递归退出条件:path.size==num.size/k 获得划分完毕的组数

代码参考:

  1 #define CNTSIZE 17
  2 class Solution {
  3 public:
  4    // int count = 0;
  5    // void print_intend() {
  6    //     for(int i=0; i<count; i++) {
  7    //         printf("  ");
  8    //     }
  9    // }
 10    // void print_path(vector<vector<int>>& path) {
 11    //     print_intend();
 12    //     printf("PATH: {");
 13    //     for(vector<int> aSet: path) {
 14    //         printf("{%d, %d} ", aSet[1], aSet[0]);
 15    //     }
 16    //     printf("}\n");
 17    // }
 18    // void print_cout(int (&cout)[CNTSIZE]) {
 19    //     print_intend();
 20    //     printf("cout: {");
 21    //     for(int i=0; i<CNTSIZE; i++){
 22    //         if(cout[i]!=0) {
 23    //             printf("[%d]=%d, ",i,cout[i]);
 24    //         }
 25    //     }
 26    //     printf("}\n");
 27    // }
 28    // void print_opt(vector<int>& opt) {
 29    //     print_intend();
 30    //     printf("opt: {");
 31    //     for(int i:opt){
 32    //         printf("%d, ",i);
 33    //     }
 34    //     printf("}\n");
 35    // }
 36     int subsize=0;
 37     int incomp(vector<vector<int>>& path) {
 38         int res = 0;
 39         for(vector<int> aSet: path) {
 40             res += aSet[1] - aSet[0];
 41         }
 42         return res;
 43     }
 44     bool isValid(int mask, int (&cout)[CNTSIZE], vector<int>& opt) {
 45         for(int i=0; i<CNTSIZE; i++) {
 46             if((mask>>i&1) == 1) {
 47                 if(cout[i]==0) return false;
 48                 opt.push_back(i);
 49             }
 50         }
 51         if(opt.size()<=0) return false;
 52         return true;
 53     }
 54     void chooseOpt(vector<vector<int>>& path, int (&cout)[CNTSIZE], vector<int>& opt) {
 55         int minV = opt[0], maxV = opt[0];
 56         for(int i=0; i<opt.size(); i++) {
 57             minV = min(minV, opt[i]);
 58             maxV = max(maxV, opt[i]);
 59             cout[opt[i]]--;
 60         }
 61         path.push_back({minV, maxV});
 62         return;
 63     }
 64     void resetOpt(vector<vector<int>>& path, int (&cout)[CNTSIZE], vector<int>& opt) {
 65         path.pop_back();
 66         for(int i=0; i<opt.size(); i++) {
 67             cout[opt[i]]++;
 68         }
 69         return;
 70     }
 71     void backtrack(int& res, vector<vector<int>>& path, int (&cout)[CNTSIZE], int k) {
 72         if(path.size()==k) {
 73            // print_intend();
 74            // printf("before res: %d.\n", res);
 75             res = min(res, incomp(path));
 76            // print_intend();
 77            // printf("after res: %d.\n", res);
 78             return;
 79         }
 80         int mask = 1<<CNTSIZE;
 81         for(int i=0; i<mask; i++) {
 82             vector<int> opt;
 83             if(__builtin_popcount(i) == subsize && isValid(i, cout, opt)) {
 84                 //get one combination
 85                 chooseOpt(path, cout, opt);
 86            // print_intend();
 87            // printf("subsie:%d, mask_i:%d\n", subsize, i);
 88            // print_opt(opt);
 89            // print_path(path);
 90            // print_cout(cout);
 91            // count++;
 92                 backtrack(res, path, cout, k);
 93            // count--;
 94                 resetOpt(path, cout, opt);
 95             }
 96         }
 97            // print_intend();
 98            // printf("return... \n");
 99         return;
100     }
101     int minimumIncompatibility(vector<int>& nums, int k) {
102         int res = INT_MAX;
103         subsize = nums.size()/k;
104         vector<vector<int>> path;
105         int cout[CNTSIZE]={0};
106         for(int n:nums){
107             cout[n]++;
108         }
109         backtrack(res, path, cout, k);
110         return (res==INT_MAX?-1:res);
111     }
112 };

 

解法二:dp结果保存

dp[map]:选择状态为map的时候,做任意分组,可获得的最小差分和结果

num.size的元素,提取任意个数,做组合:从0->(1<<num.size-1)

其中选择个数为subsize=num.size/k 的组合为要求的子数组的一个选择。

 

首先初始化,任意subsize个元素构成子数组后的,最小差分和结果:

1         for(int i=0; i<(1<<n); i++) {
2             if(__builtin_popcount(i) == subsize) {
3                 set<int> grp;
4                 for(int j=0; j<n; j++) {
5                     if(((i>>j)&1)==1) grp.insert(nums[j]);
6                 }
7                 if(grp.size()==subsize) dp[i] = *prev(grp.end()) - *grp.begin();
8             }
9         }

 

然后 从选择元素个数>一个子数组subsize个之后开始,逐次计算

当前选择元素状态下的,最小差分和。

i:从2组子数组的选择开始:

  j:在 i 的这种构成中,其中获得一个组 j,可得到的一个选择结果。

  若:这个组 j 没有结果 || 除去这个组剩下元素构成的组 i-j 也没有结果 || j 没构成一个组(j的元素数!=subsize)

    那么这个选择无效,跳过。

  否则:dp[i] = dp[j] + dp[i-j]

  若dp[i]以及保存过解,那么说明,之前 i 的这种构成中,已经找到过一个合法的 分裂组合 j 和 i-j。那么在现在的结果和新结果中求最小:min(dp[i], dp[j]+dp[i-j])

 

最终求得,dp[11111111...1] 所有元素被选择完,得到的最优解。

 

代码参考:

 1 class Solution {
 2 public:
 3     int subsize=0;
 4     int minimumIncompatibility(vector<int>& nums, int k) {
 5         int res = INT_MAX;
 6         int n = nums.size();
 7         subsize = n/k;
 8         vector<int>dp(1<<n, INT_MAX);
 9         //initialize the incomp of each kind of grp.
10         for(int i=0; i<(1<<n); i++) {
11             if(__builtin_popcount(i) == subsize) {
12                 set<int> grp;
13                 for(int j=0; j<n; j++) {
14                     if(((i>>j)&1)==1) grp.insert(nums[j]);
15                 }
16                 if(grp.size()==subsize) dp[i] = *prev(grp.end()) - *grp.begin();
17             }
18         }
19         
20         for(int i=0; i<(1<<n); i++) {
21             if(__builtin_popcount(i) % subsize != 0 || __builtin_popcount(i) == subsize) 
22                 continue;
23             //处理:整组,&& 是2组以及2组以上
24             for(int j=i; j; j=(j-1)&i) {//当前情况的内部:j构成一组,处理
25                 if(__builtin_popcount(j) != subsize || dp[j] == INT_MAX || dp[i-j] == INT_MAX)
26                     continue;
27                 dp[i] = min(dp[i], dp[j] + dp[i-j]);
28             }
29         }
30         res = dp[(1<<n)-1];
31         return res==INT_MAX?-1:res;
32     }
33 };

 

posted @ 2021-02-21 15:07  habibah_chang  阅读(104)  评论(0编辑  收藏  举报