zoukankan      html  css  js  c++  java
  • 01背包、完全背包、多重背包

    参考(都有些错误):
    https://github.com/guanjunjian/Interview-Summary/blob/master/notes/algorithms/%E7%BB%8F%E5%85%B8%E7%AE%97%E6%B3%95/01%E8%83%8C%E5%8C%85.md
    https://blog.csdn.net/na_beginning/article/details/62884939
    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    /*
    01背包问题 每个物品仅一个
    状态转移公式:F[i][j] = F[i - 1][j] 和 F[i - 1][j - W[i]] + V[i] 大的那个值
    C背包总重量
    W每个物品重量
    V每个物品价值
    n物品总数
    inp具有最大价值时,标记哪个物品在包中
    返回最大价值
    */
    int packages(int C, vector<int> &W, vector<int> &V, int n, vector<int> &inp)
    {
        vector<vector<int> > F(n, vector<int>(C + 1));//F[i][j]记录背包可用重量为j时,前i个物品的最大价值
        
        for (int i = 0; i < n; i++)//初始化表格
        {
            for (int j = 0; j < F[0].size(); j++)
                F[i][j] = 0;
        }
        for (int j = 1; j < F[0].size(); j++)//初始化第一行
            F[0][j] = (j < W[0]) ? 0 : V[0];//可用重量j大于物品重量,则装入该物品,加上该物品价值
    
        for (int i = 1; i < n; i++)
        {
            for (int j = 1; j < F[0].size(); j++)
            {
                if (j < W[i])
                    F[i][j] = F[i - 1][j];//装不下该物品
                else
                    F[i][j] = (F[i - 1][j] < F[i - 1][j - W[i]] + V[i]) ? F[i - 1][j - W[i]] + V[i] : F[i - 1][j];//可装下该物品。状态转移方程
            }
        }
        int result = F[n - 1][F[0].size() - 1];//最大价值
                                               
        //inp标记哪些物品在包中
        int j = C;
        for (int i = n-1; i >0 ; i--)//i不可为0,否则F[i-1]越界
        {
            if (j>W[i] && F[i][j] == F[i - 1][j - W[i]] + V[i])//注意需要j>W[i]
            {
                inp[i] = 1;
                j -= W[i];
            }
        }
        if (F[0][j] > 0)//如果背包还可装下,则包含第一个物品
            inp[0] = 1;
    
        //输出动态规划数组
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < F[0].size(); j++)
                cout << F[i][j] << " ";
            cout << endl;
        }
    
        return result;
    }
    
    /*
    01背包问题 滚动数组优化动态规划的空间
    原空间OnC,先空间O2C,但是无法输出有哪些物品在包中
    */
    int packages_good(int C, vector<int> &W, vector<int> &V, int n)
    {
        vector<vector<int> > F(2, vector<int>(C + 1));//F[i][j]记录背包可用重量为j时,前i个物品的最大价值
    
        for (int i = 0; i < 2; i++)//初始化表格
        {
            for (int j = 0; j < F[0].size(); j++)
                F[i][j] = 0;
        }
        for (int j = 1; j < F[0].size(); j++)//初始化第一行
            F[0][j] = (j < W[0]) ? 0 : V[0];//可用重量j大于物品重量,则装入该物品,加上该物品价值
    
        int k = 0;
        for (int i = 1; i < n; i++)
        {
            k = i & 1;//滚动
            for (int j = 1; j < F[0].size(); j++)
            {
                if (j < W[i])
                    F[k][j] = F[k ^ 1][j];//装不下该物品
                else
                    F[k][j] = (F[k ^ 1][j] < F[k ^ 1][j - W[i]] + V[i]) ? F[k ^ 1][j - W[i]] + V[i] : F[k ^ 1][j];//可装下该物品
            }
        }
        return F[k][F[0].size() - 1];//最大价值
    }
    
    /*
    完全背包 每个物品不限制数量
    状态转移公式:F[i][j] = F[i - 1][j] 和 F[i][j - W[i]] + V[i] 大的那个值,标记哪些物品在包中也改变
    注意是F[i][j - W[i]] + V[i]
    也可优化空间
    */
    int packages_full(int C, vector<int> &W, vector<int> &V, int n, vector<int> &inp)
    {
        vector<vector<int> > F(n, vector<int>(C + 1));//F[i][j]记录背包可用重量为j时,前i个物品的最大价值
    
        for (int i = 0; i < n; i++)//初始化表格
        {
            for (int j = 0; j < F[0].size(); j++)
                F[i][j] = 0;
        }
        for (int j = 1; j < F[0].size(); j++)//初始化第一行
            F[0][j] = (j < W[0]) ? 0 : ((j/W[0])*V[0]);//每个物品不止一个
    
        for (int i = 1; i < n; i++)
        {
            for (int j = 1; j < F[0].size(); j++)
            {
                if (j < W[i])
                    F[i][j] = F[i - 1][j];//装不下该物品
                else
                    F[i][j] = (F[i - 1][j] < F[i][j - W[i]] + V[i]) ? F[i][j - W[i]] + V[i] : F[i - 1][j];//可装下该物品。状态转移方程
            }
        }
        int result = F[n - 1][F[0].size() - 1];//最大价值
    
        //inp标记哪些物品在包中
        int j = C;
        int i = n - 1;
        while (i)
        {
            while(j && j > W[i] && F[i][j] == F[i][j - W[i]] + V[i]) //如果包含该物品,则一直循环看包含几个该物品
            {
                inp[i]++;
                j -= W[i];
            }
            i--;//不包含该物品,则跳到下一个物品
        }
    
        //输出动态规划数组
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < F[0].size(); j++)
                cout << F[i][j] << " ";
            cout << endl;
        }
    
        return result;
    }
    
    /*
    多重背包 每个物品有数量限制
    */
    int packages_multi(int C, vector<int> &W, vector<int> &V, int n, vector<int> &inp, vector<int> &N)//N会被修改
    {
        vector<vector<int> > F(n, vector<int>(C + 1));//F[i][j]记录背包可用重量为j时,前i个物品的最大价值
    
        for (int i = 0; i < n; i++)//初始化表格
        {
            for (int j = 0; j < F[0].size(); j++)
                F[i][j] = 0;
        }
        for (int j = 1; j < F[0].size(); j++)//初始化第一行,每个物品有对应数量N限制
        {
            int count = (N[0] > j / W[0]) ? j / W[0] : N[0];//可用空间为j时,可以放多少个当前物品,min{可放入数量,物品数量}
            F[0][j] = (j < W[0]) ? 0 : (count*V[0]);
        }
    
        for (int i = 1; i < n; i++)
        {
            for (int j = 1; j < F[0].size(); j++)
            {
                if (j < W[i])
                    F[i][j] = F[i - 1][j];//装不下该物品
                else
                {
                    int count = (N[i] > j / W[i]) ? j / W[i] : N[i];
                    F[i][j] = F[i - 1][j];//不放当前物品
                    for (int k = 1; k <= count; k++)//看放入多少个当前物品可以让F[i][j]最大
                    {
                        int tmp = F[i - 1][j - k * W[i]] + k * V[i];//放入k个当前物品
                        if (tmp > F[i][j])
                            F[i][j] = tmp;
                    }
                }
            }
        }
        int result = F[n - 1][F[0].size() - 1];//最大价值
        cout << result << endl;
    
        //inp标记哪些物品在包中
        int j = C;
        int i = n - 1;
        while (i)
        {
            while (j && N[i] && j > W[i] && F[i][j] == F[i][j - W[i]] + V[i]) //如果包含该物品,则一直循环看包含几个该物品
            {
                inp[i]++;
                j -= W[i];
                --N[i];
            }
            i--;//不包含该物品,则跳到下一个物品
        }
    
        //输出动态规划数组
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < F[0].size(); j++)
                cout << F[i][j] << " ";
            cout << endl;
        }
    
        return result;
    }
    
    int main() {
        vector<int> W{ 4,5,6,2,2 };
        vector<int> V{ 6,4,5,3,6 };
        int C = 9;
        int n = W.size();
        vector<int> inp(n, 0);//标记是否在包中
        cout << "1、01背包最大总价值:" << packages(C, W, V, n, inp) << endl;
        cout << "在背包中的物品编号: ";
        for (int i = 0; i < inp.size(); i++)
        {
            if (inp[i] == 1)
                cout << i << " ";
        }
        cout << endl;
        cout << "2、01背包滚动数组优化最大总价值,无法输出有哪些物品在背包中:" << packages_good(C, W, V, n) << endl;
    
        cout << endl;
        vector<int> inp1(n, 0);//标记是否在包中
        cout << "3、完全背包最大总价值:" << packages_full(C, W, V, n, inp1) << endl;
        cout << "输出在背包中物品: ";
        for (int i = 0; i < inp1.size(); i++)
        {
            cout << inp1[i] << " ";
        }
        cout << endl;
            
        cout << endl;
        vector<int> N{ 1,2,2,2,4 };//每个物品数量
        vector<int> inp2(n, 0);//标记是否在包中
        cout << "4、多重背包最大总价值:" << packages_multi(C, W, V, n, inp2, N) << endl;
        cout << "输出在背包中物品: ";
        for (int i = 0; i < inp2.size(); i++)
        {
            cout << inp2[i] << " ";
        }
        cout << endl;
    
    
        return 0;
    }
  • 相关阅读:
    Java基础(六)判断两个对象相等:equals、hashcode、toString方法
    同时找最大最小值
    0-1背包问题
    大数相加
    单例模式(singleton pattern)
    House Robber
    Binary Tree Paths
    双向链表的插入
    工厂模式(factory pattern)
    装饰者模式(decorator pattern)
  • 原文地址:https://www.cnblogs.com/beixiaobei/p/10914211.html
Copyright © 2011-2022 走看看