leetcode 1681

可以用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];
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值