溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++動(dòng)態(tài)規(guī)劃中關(guān)于背包問(wèn)題怎么解決

發(fā)布時(shí)間:2023-03-15 11:56:07 來(lái)源:億速云 閱讀:94 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容主要講解“C++動(dòng)態(tài)規(guī)劃中關(guān)于背包問(wèn)題怎么解決”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“C++動(dòng)態(tài)規(guī)劃中關(guān)于背包問(wèn)題怎么解決”吧!

一、分割等和子集-最后一塊石頭的重量II

背包問(wèn)題,難點(diǎn)往往在第一步:dp數(shù)組表示什么

分割等和子集問(wèn)題,較好的方式是:求裝滿背包后最大重量是多少(有點(diǎn)繞哈哈)

這是個(gè)題型:對(duì)于判斷能不能恰好裝滿背包的問(wèn)題,用dp表示重量,判斷是否最終的dp[m]==m

bool canPartition(int* nums, int numsSize){
    //首先數(shù)組元素求和的sum,若sum%2==1,返回false
    //若sum%2==0,定義m=sum/2,n=numsSize
    //則問(wèn)題變成了能否裝滿容量為m的背包
    //進(jìn)一步變成了求裝滿容量為m的背包得到的最大價(jià)值量(本題價(jià)值量即為重量)
    //1.dp[j]表示裝滿容量為j的背包能獲得的最大價(jià)值量
    //2.遞推式:dp[j]=fmax(dp[j],dp[j-nums[i]]+nums[i]);
    //3.dp數(shù)組初始化:dp[i]=0;
    //4.遍歷順序:0-1背包順序(滾動(dòng)數(shù)組)
    int sum=0;
    for(int i=0;i<numsSize;i++) sum+=nums[i];
    if(sum%2==1) return false;
    int m=sum/2,n=numsSize;
    int dp[m+1];
    for(int j=0;j<=m;j++) dp[j]=0;
    for(int i=0;i<n;i++){
        for(int j=m;j>=nums[i];j--)
            dp[j]=fmax(dp[j],dp[j-nums[i]]+nums[i]);
    }
    if(dp[m]==m) return true;
    else return false;
}

二、目標(biāo)和

求組合數(shù)模板:dp[0]=1;dp[j]+=dp[j-nums[i]];

int findTargetSumWays(int* nums, int numsSize, int target){
    //首先數(shù)組元素求和的sum,若滿足題意,m+(m-target)=sum
    //若(sum+target)%2==1,返回0;
    //若sum<abs(target),返回0;
    //否則,有m=(sum+target)/2;
    //問(wèn)題就變成了整數(shù)m可以有多少表達(dá)式表示出
    //進(jìn)一步變成了求裝滿容量為m的背包的最大組合數(shù)
    //1.dp[j]表示裝滿容量為j的背包的最大表達(dá)式的組合數(shù)
    //2.遞推式:
    //組合問(wèn)題模板:dp[0]=1;dp[j]+=dp[j-nums[i]];
    //3.dp數(shù)組初始化:dp[i]=0;dp[0]=1;
    int sum=0;
    for(int i=0;i<numsSize;i++) sum+=nums[i];
    if(sum<abs(target)||(sum+target)%2==1) return 0;
    int m=(sum+target)/2,n=numsSize;
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int i=0;i<n;i++){
        for(int j=m;j>=nums[i];j--)
            dp[j]+=dp[j-nums[i]];
    }
    return dp[m];
}

三、一和零

注意二維滾動(dòng)數(shù)組不能寫(xiě)在同一個(gè)for循環(huán)中,這題背一下

int findMaxForm(char ** strs, int strsSize, int m, int n){
    //本題是二維背包,不過(guò)是比一維多了一步而已
    //1.dp[i][j]表示背包容量為i個(gè)0、j個(gè)1時(shí),最多能裝的物品個(gè)數(shù)
    //2.遞推式:
    //dp[i][j]=fmax(dp[i][j],dp[i-cnt0][j-cnt1]+1);
    //3.dp數(shù)組初始化:
    //dp[i][j]=0;
    //4.遍歷順序:二維滾動(dòng)數(shù)組(注意不能把i和j寫(xiě)在同一個(gè)for循環(huán)中)
    int dp[m+1][n+1];
    for(int i=0;i<=m;i++){
        for(int j=0;j<=n;j++)
            dp[i][j]=0;
    }
    for(int k=0;k<strsSize;k++){
        int cnt0=0,cnt1=0;
        int len=strlen(strs[k]);
        for(int i=0;i<len;i++){
            if(strs[k][i]=='0') cnt0++;
            else cnt1++;
        }
        for(int i=m;i>=cnt0;i--){
            for(int j=n;j>=cnt1;j--){
                dp[i][j]=fmax(dp[i][j],dp[i-cnt0][j-cnt1]+1);
            }
        }
    }
    return dp[m][n];
}

四、零錢(qián)兌換II

多重背包和0-1背包唯一的區(qū)別在遍歷順序

我們知道01背包內(nèi)嵌的循環(huán)是從大到小遍歷,為了保證每個(gè)物品僅被添加一次。

而完全背包的物品是可以添加多次的,所以要從小到大去遍歷

int change(int amount, int* coins, int coinsSize){
    int m=amount,n=coinsSize;
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int i=0;i<n;i++){
        for(int j=coins[i];j<=m;j++)
            dp[j]+=dp[j-coins[i]];
    }
    return dp[m];
}

五、排列與組合

組合總數(shù)IV(排列問(wèn)題)

本題要求的是排列數(shù)(即考慮排列順序)

求排列數(shù),外層遍歷重量,內(nèi)層遍歷物品,且均為從左到右遍歷

int combinationSum4(int *nums,int n,int m){
    //1.dp[j]表示背包容量為j時(shí),有多少種方法能使背包被裝滿“
    //2.遞推式:
    //dp[j]+=dp[j-nums[i]];
    //3.初始化:
    //dp[i]=0;dp[0]=1;
    //4.遍歷順序:
    //本題要求的是排列數(shù)(即考慮排列順序)
    //求排列數(shù),外層遍歷重量,內(nèi)層遍歷物品,且均為從左到右遍歷
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int j=0;j<=m;j++){
        for(int i=0;i<n;i++){
            if(j>=nums[i]&&dp[j]<INT_MAX-dp[j-nums[i]])
                dp[j]+=dp[j-nums[i]];
        }
    }
    return dp[m];
}

零錢(qián)兌換(組合問(wèn)題)

本題要求的是組合數(shù)(即不考慮排列順序)

求組合數(shù),外層遍歷物品,內(nèi)層遍歷重量,且均為從左到右遍歷

int int coinChange(int* coins, int coinsSize, int amount){
    //1.dp[j]表示背包容量為j時(shí),有多少種方法能使背包被裝滿“
    //2.遞推式:
    //dp[j]+=dp[j-coins[i]];
    //3.初始化:
    //dp[i]=0;dp[0]=1;
    //4.遍歷順序:
    //本題要求的是組合數(shù)(即不考慮排列順序)
    //求組合數(shù),外層遍歷物品,內(nèi)層遍歷重量,且均為從左到右遍歷
    int m=amount,n=coinsSize;
    int dp[m+1];
    for(int i=1;i<=m;i++) dp[i]=0;
    dp[0]=1;
    for(int i=0;i<n;i++){
        for(int j=coins[i];j<=m;j++)
            dp[j]+=dp[j-coins[i]];
    }
    return dp[m];
}

到此,相信大家對(duì)“C++動(dòng)態(tài)規(guī)劃中關(guān)于背包問(wèn)題怎么解決”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI