dp小結|背包問題


1.先放上0-1背包模板

二維數組

for(int i=1;i<=n;i++)//枚舉 物品 
    for(int j=1;j<=V;j++)//枚舉體積 
    //這個位置是可以正序枚舉的.  qwq
    //一維01背包必須倒敘  emmm
    //這個沒錯a emmm  
        if(j>=c[i])
            f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]);//狀態轉移方程.
        else f[i][j]=f[i-1][j].

滾動二維數組

    int dp[2][10010];
    int row = 0; //滾動 
    for (int i = 1; i < n; ++i) {
        row = 1 - row;
        for (int j = 1; j <= i * (i + 1) / 2; ++j) {//這里可以減少枚舉
        if(j>=c[i])
            f[row][j]=max(f[1-row][j],f[1-row][j-c[i]]+w[i]);//狀態轉移方程.
        else f[row][j]=f[1-row][j].
        }
    }

一維數組(逆序)

for(int i=1;i<=n;i++)//枚舉 物品 
    for(int j=V;j>=c[i];j--)//枚舉體積 
        f[j]=max(f[j],f[j-c[i]]+w[i]);//狀態轉移方程. 

模板題:洛谷:P1048采葯
“采葯”這道題套模板就行了,從二維到一維優化。

2.順便再復習記憶化搜索洛谷:P1048采葯記憶化搜索做法題解

先想到dfs搜索暴力解題,就是多參數遞歸,出口記錄搜索到的值
再想到記憶化搜索,什么時記憶化搜索?記錄每一次dfs答案,免去重復計算,從而起到優化時間復雜度的作用
記憶化搜索記錄什么值呢?記錄每次搜索結束找到的價值(不一定最大)

如何想到可以用記憶化搜索?

3.藍橋杯考過的記憶化搜索——2013年C++B組第9題地宮取寶

使用四維數組緩存記錄。

4.藍橋杯考過的0-1背包:2014年C++A組第十題-波動數列

這道題比較難想到0-1背包,選數字對應成拿物品填充背包,dp[i][j]表示用前i個數湊出數字j的方案數,類似題目:我記得有一道選幾個數字,湊出數字n。初始化dp[0][0] = 1,第0列都等於1,dp[i~n][0] = 1

5.完全背包

與0-1背包略有不同的是,每種物品有無限多個,可重復選取。

二維數組,三層循環模板

for(int i=1;i<=n;i++)//枚舉物品
    for(int j=1;j<=V;j++)
        for(int k=1;k<=V/c[i];k++)//我們的物品最多只能放多少件.  
            {
                if(k*c[i]<=j)
                    f[i][j]=max(f[i-1][j],f[i-1][j-k*c[i]]+k*w[i]);
                else 
                    f[i][j]=f[i-1][j];
                 //判斷條件與01背包相同.
            }

一維數組,兩層循環模板(順序枚舉,和0-1背包一維恰好相反)

for(int i=1;i<=n;i++)//枚舉物品
    for(int j=c[i];j<=V;j++)//枚舉體積.注意這里是順序/
        f[j]=max(f[j],f[j-c[i]]+w[i]);//狀態轉移.

完全背包模板題:P1616 瘋狂的采葯

6.藍橋杯考過的完全背包:2017年藍橋杯-包子湊數

1~100000(比較大的值)
初始化dp[0] = 1
完全背包思想,推出布爾類型的dp數組,最后遍歷最后dp數組最后一層為true的個數

整數划分(每個數可以用無限次)——完全背包

#include<bits/stdc++.h>
using namespace std;

const int maxn = 100005;
int n;
int dp[maxn];

int main(){
	cin>>n;
	dp[0] = 1;
	for(int i=1;i<=n;i++){//可以用到n 
		for(int j=i;j<=n;j++){//完全背包 每個數字可以用無限次 
			dp[j] = dp[j] + dp[j-i];
		}
	}
	cout<<dp[n]<<endl;
	return 0;
} 

整數划分(每個數只能用一次)——0-1背包

#include<bits/stdc++.h>
using namespace std;

const int maxn = 100005;
int n;
int dp[maxn];

int main(){
	cin>>n;
	dp[0] = 1;
	for(int i=1;i<=n-1;i++){//不能用n 
		for(int j=n;j>=i;j--){//0-1背包 倒序 
			dp[j] = dp[j] + dp[j-i];
		}
	}
	cout<<dp[n]<<endl;
	return 0;
} 
//輸入6
//輸出3  說明:1、5;2、4;1、2、3 

7.多重背包,沒做過題不好講復習。。

多重背包問題限定了一種物品的個數,一個簡單的方法,轉成0-1背包。先這樣寫吧

#include <iostream>
using namespace std;
#define V 1000
int weight[50 + 1];
int value[50 + 1];
int num[20 + 1];
int f[V + 1];
int max(int a, int b) {
    return a > b ? a : b;
}
int main() {
    int n, m;
    cout << "請輸入物品個數:";
    cin >> n;
    cout << "請分別輸入" << n << "個物品的重量、價值和數量:" << endl; 
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i] >> num[i];
    }
    int k = n + 1;
    for (int i = 1; i <= n; i++) {
        while (num[i] != 1) {
            weight[k] = weight[i];
            value[k] = value[i];
            k++;
            num[i]--;
        }
    }
    cout << "請輸入背包容量:";
    cin >> m;
    for (int i = 1; i <= k; i++) {
        for (int j = m; j >= 1; j--) {
            if (weight[i] <= j) f[j] = max(f[j], f[j - weight[i]] + value[i]);
        }
    }
    cout << "背包能放的最大價值為:" << f[m] << endl;
}

學長的dp:https://blog.csdn.net/o_ohello/article/details/89378697


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM