可以用0表示不取数字,1表示取数字,那么一共有1<<nums种情况。用i的二进制表示其选取的数字,可以定义转态dp[i][j]:i这个数字其选取的数字在分成j组的情况下的最小不兼容性。可得转态转移方程:dp[i][j]=min(dp[i][j],dp[s][1]+dp[i-s][j-1]),其中s为i的子集并且s的二进制表示一定要有且仅有k个1。
int solve(vector<int>& v){//计算对应的十进制数字
int res=0;
for(int i=0;i<v.size();i++){
if(v[i]==1)
res+=(1<<i);
}
return res;
}
int judge(vector<int>&v,vector<int>&nums){//计算v这一组数据的不兼容性
set<int>st;
for(int i=0;i<v.size();i++){
if(v[i]){
if(st.count(nums[i]))
return -1;
st.insert(nums[i]);
}
}
return *st.rbegin()-*st.begin();
}
int minimumIncompatibility(vector<int>& nums, int k) {
int res=0,dp[100000][20],n=nums.size();//dp[i][j]表示i表示的数字分成j组的最小不兼容性
vector<int>one;//记录一组数据的所有十进制表示,用于寻找i的子集,暴力会tle
for(int i=1;i<=(1<<n);i++)
for(int j=1;j<=k;j++)
dp[i][j]=-1;
for(int j=1;j<=k;j++){
vector<int>v;
for(int i=1;i<=n-j*(n/k);i++)
v.push_back(0);
for(int i=1;i<=j*(n/k);i++)
v.push_back(1);
if(j==1){
int i=solve(v);
one.push_back(i);
dp[i][j]=judge(v,nums);
while(next_permutation(v.begin(),v.end())){
int i=solve(v);
one.push_back(i);
dp[i][j]=judge(v,nums);
}
}
else{
int i=solve(v);
for(int t=0;t<one.size();t++){
int s=one[t];
if((s&i)!=s)//s不是i的子集则继续
continue;
if(dp[s][1]!=-1 && dp[i-s][j-1]!=-1){//状态转移
if(dp[i][j]==-1)
dp[i][j]=dp[s][1]+dp[i-s][j-1];
else
dp[i][j]=min(dp[i][j],dp[s][1]+dp[i-s][j-1]);
}
}
while(next_permutation(v.begin(),v.end())){
int i=solve(v);
for(int t=0;t<one.size();t++){
int s=one[t];
if((s&i)!=s)
continue;
if(dp[s][1]!=-1 && dp[i-s][j-1]!=-1){
if(dp[i][j]==-1)
dp[i][j]=dp[s][1]+dp[i-s][j-1];
else
dp[i][j]=min(dp[i][j],dp[s][1]+dp[i-s][j-1]);
}
}
}
}
}
return dp[(1<<n)-1][k];
}