01背包算法

来源:互联网 发布:c语言集合的交并运算 编辑:程序博客网 时间:2024/06/03 15:37

核心思想
知道背包的容量C,知道物品数量N,和物品的价值V[N+1]重量W[N+1],创建一个二阶矩阵m[N+1][C+1],其中第一行第一列不用,因为他的下标保证和i个物品一致
第一步:那么先从最后一个物品看一个一个看
容量从1~C开始看
如果最后一个物品能放入背包,就把它放进去,不能装的时候m[N][i]都是0,能装的时候就是最后一个物品价值的值
第二步:开始查看倒数第二个物品
当不能装入倒数第二个物品的时候,就把倒数第一个物品的值都往上移动,比如说如果倒二太大,不能装,但是倒一能装,那么这两个物品装入背包的最大价值就是倒一
当倒二可以装的话,那么当前容量减去倒二的质量,然后看倒一那一行的价值加上倒二的价值和倒一的价值比谁大,取大的那一个,防止如果装了倒二不能装倒一而值反而小了的情况
第三步:循环使用第二步
最后最大的背包容量的值在m[1][C]

关于查找
直接从第一行查找,如果第一行到第二行最大值变了那就说明第一个物品在背包,依次查到最后一行。
最后一行再进行判断,如果它非0那么就说明它也在背包里面

代码如下

#include<iostream>#include<iomanip>using namespace std;#include<malloc.h>#include<string.h>#include<stdlib.h>int * GetArray(int n){    int *s = (int*)malloc(sizeof(int)*n);    if (NULL == s) exit(1);    memset(s, 0, sizeof(int)*n);    return s;}//获取一个n个单位的数组void FreeArray(int *p){    free(p);}//内存释放,防止内存泄漏void Print_Array(int *p, int n){    for (int i = 0; i < n; ++i)    {        cout << p[i] << " ";    }    cout << endl;}//打印数组int ** Get2Array(int n, int m){    int **s = (int**)malloc(sizeof(int*)*n);    for (int i = 0; i < n; ++i)    {        s[i] = (int*)malloc(sizeof(int)*m);        memset(s[i], 0, sizeof(int)*m);    }    return s;}//获取一个n*m的二维数组,并将其中的每个元素都设置为0void Free2Array(int **p, int n){    for (int i = 0; i < n; ++i)    {        free(p[i]);    }    free(p);}//释放一个二维数组的空间void Print_2Array(int **p, int n, int m){    for (int i = 0; i < n; ++i)    {        for (int j = 0; j < m; ++j)        {            cout << setw(3) << p[i][j];        }        cout << endl;    }    cout << endl;}//打印二维数组//////////////////////////////////////int Knapsack(int *W, int *V, int n, int c, int **m){//n = 5 c = 10 w是重量 v是价值 c是容量 n是数目    //int W[N + 1] = { 0,2,2,6,5,4 }    //int V[N + 1] = { 0,6,3,5,4,6 }    int i, j;    for (j = 1; j <= c; ++j)//    {        if (j >= W[n])//j = 1~10            m[n][j] = V[n];        else            m[n][j] = 0;//m[5][1~10]    }//仅仅针对最后一个物品,我有的容量的最大值是    Print_2Array(m, n + 1, c + 1);    for (i = n - 1; i >= 1; --i)//从(下标)n-1行到第一行    {        for (j = 1; j <= c; ++j)//开始有两个物品        {            if (j < W[i])//j < 下标为i的重量                m[i][j] = m[i + 1][j];//容量小于i物品的重量,就把之前的值放上来            else            {//拿之前的m的值和 减去当前物品重量的值加上当前物品价值的值比较                m[i][j] = m[i + 1][j] > m[i + 1][j - W[i]] + V[i] ?                    m[i + 1][j] : m[i + 1][j - W[i]] + V[i];            }        }        Print_2Array(m, n + 1, c + 1);    }    return m[1][c];//最后的m[1][c]就是他这个背包能够装载的最大值的值}void Traceback(int *W, int **m, int n, int c, int *X){    //n是物品数量 5     //c是背包容量 10    //m存放刚得出的二阶矩阵    //W是重量    int i;    for (i = 1; i < n; ++i)//1 ~ 5    {        if (m[i][c] != m[i + 1][c])        {//如果最后的最大值发生改变了,说明i物品被放进去了会改变最大,所以i物品进入了最后的背包            X[i] = 1;            c -= W[i];//重量减去i物品的重量        }    }    if (i == n)//如果i==n那就遍历完了,但是还不知道最后一个有没有装进去    {        if (m[i][c] != 0)//如果最后一个不等于0那么就说明他也被装入了背包        {            X[i] = 1;        }    }}void main(){    const int N = 5;//五个物品    const int C = 10;//容量    int **m = Get2Array(N + 1, C + 1);//建立一个6*11的二维矩阵,第一列第一行不用    int W[N + 1] = { 0,2,2,6,5,4 };//五个物品的重量    int V[N + 1] = { 0,6,3,5,4,6 };//五个物品的价值    int X[N + 1] = { 0,0,0,0,0,0 };    int maxv = Knapsack(W, V, N, C, m);    cout << maxv << endl;    Print_2Array(m, N + 1, C + 1);    Traceback(W, m, N, C, X);    for (int i = 1; i <= N; ++i)//拿哪些物品    {        if (X[i])        {            cout << i << " ";        }    }    cout << endl;}

拓展
小萌是个WOW发烧友,每天都痴迷于他的法师号。精诚所至金石为开,小萌穿越到WOW的世界中了…
初来乍到的小萌在暴风城的小巷中,遇见了一位善良的德鲁伊。德鲁伊听了小萌的故事,打算帮助他在WOW这个世界好好活下去,于是,把自己的东西都给了小萌了…
德鲁伊的东西太多了,于是小萌去拍卖行买了几个包裹,一切妥当之后,小萌开始把东西装进包裹里。
不过,因为小萌穿越时候脑袋先着地,所以脑子不好用,每次他拿起一个物品,要不装进包里,要不就直接扔掉…
而且,一个背包一旦不往里装东西,小萌就会封上口不再用…
现在,告诉你小萌每个物品的体积,背包的个数和容量,以及小萌拿物品的顺序,你要帮助小萌求出他能拿走多少东西。

#include <iostream>#include <string.h>#include <vector>#include <algorithm>using namespace std;int main(){    int N;//物品个数    int T;//背包容量    int M;//背包个数    int b[25][25][25];    cin >> N >> T >> M;    int *arr = new int[N + 1];    arr[0] = 0;    for (int i = 1; i < N + 1; ++i)    {        cin >> arr[i];    }    memset(b, 0, sizeof(b));    for (int i = 1; i <= M; ++i)//背包个数    {        for (int j = 1; j <= N; ++j)//物品个数        {            for (int k = 1; k <= T; ++k)            {                if (k >= arr[j])                {                    b[i][j][k] = max(max(b[i][j - 1][k], b[i][j - 1][k - arr[j]] + 1), b[i - 1][j][T]);                }                else                {                    b[i][j][k] = max(b[i][j - 1][k], b[i - 1][j][T]);                }            }        }    }    cout << b[M][N][T] << endl;    return 0;}

给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)
例如:给定一个长度为8的数组A{1,3,5,2,4,6,7,8},则其最长的单调递增子序列为{1,2,4,6,7,8},长度为6.

#include <iostream>using namespace std;int getMax(int *arr, int len){    int b[100] = { 0 };//可以动态但是关键是算法思路所以我就不纠结细节了    b[0] = 1;    int result = 1;    for (int i = 1; i < len; ++i)    {        int max = 1;        for (int j = i - 1; j >= 0; --j)        {            int tmp = 0;            if (arr[i] > arr[j])            {                tmp = b[j] + 1;            }            if (tmp > max)            {                max = tmp;            }        }        b[i] = max;        if (max > result)        {            result = max;        }    }    return result;}int main(){    int arr[8] = { 1,3,5,2,4,6,7,8 };    cout << getMax(arr, 8) << endl;    return 0;}
0 0