背包问题

一、01背包

现在有一个背包(容器),它的体积(容量)为V,现在有N种物品(每个物品只有一个),每个物品的价值v[i]和占用空间w[i]都会由输入给出,现在问这个背包最多能携带总价值多少的物品?

输入 4 5

 3 2 4 2 2 1 3 2

输出 7

解析:此题可以用dp来做。因为每个物品都有取与不取两种情况,所以如果只看N的化会出现复杂度为2的n次方的爆炸情况,所以此地方需要考虑N和V

状态:a[i][j]表示到第i个物体为止,需要占用的最大空间为j。//注意这个地方是最大,所以循环中有一个比较问题,因为如果没占满空间的价值也有可能比原来高

转移:if(v>=w[i]) a[i][j]=max(a[i-1][j-w[i])+v[i],a[i-1][j]);//占满与没占满的比较

代码://空间复杂度为N*M,时间复杂度为N*M

#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 110
using  namespace std;
int N,W,v[maxn],w[maxn],s[maxn][10100];
int main()
{
    while(cin>>N>>W&&W)
    {
        for(int i=1;i<=N;i++)
            cin>>v[i];
        for(int i=1;i<=N;i++)
            cin>>w[i];
        memset(s,0,sizeof(s));
        for(int i=1;i<=N;i++)
        {
            for(int j=W;j>=0;j--)
            {
                if(j>=w[i])
                    s[i][j]=max(s[i-1][j-w[i]]+v[i],s[i-1][j]);
                else
                    s[i][j]=s[i-1][j];
            }
        }
        cout<<s[N][W]<<endl;
    }
    return 0;
}

改进:空间复杂度为W,二维坐标来回倒腾//如果设个k的话,注意++的位置在第一层循环,而不是第二层循环

#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 110
using  namespace std;
int N,W,v[maxn],w[maxn],s[2][10100];
int main()
{
    while(cin>>N>>W&&W)
    {
        for(int i=1;i<=N;i++)
            cin>>v[i];
        for(int i=1;i<=N;i++)
            cin>>w[i];
        memset(s,0,sizeof(s));
        int k=0;
        for(int i=1;i<=N;i++)
        {
            for(int j=W;j>=0;j--)
            {
                if(j>=w[i])
                    s[i%2][j]=max(s[(i-1)%2][j-w[i]]+v[i],s[(i-1)%2][j]);//装还是不装
                else
                    s[i%2][j]=s[(i-1)%2][j];
            }
        }
        cout<<s[k%2][W]<<endl;
    }
    return 0;
}
改进:空间复杂度为W只用一维数组

#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 110
using  namespace std;
int N,W,v[maxn],w[maxn],s[10100];
int main()
{
    while(cin>>N>>W&&W)
    {
        for(int i=1;i<=N;i++)
            cin>>v[i];
        for(int i=1;i<=N;i++)
            cin>>w[i];
        memset(s,0,sizeof(s));
        for(int i=1;i<=N;i++)
        {
            for(int j=W;j>=0;j--)
            {
                if(j>=w[i])
                    s[j]=max(s[j-w[i]]+v[i],s[j]);
            }
        }
        cout<<s[W]<<endl;
    }
    return 0;
}
二、完全背包

完全背包:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

//input
//3 7
//4 5 3
//3 4 2
//output
//10
//01背包:递推关系:s[i][j]=max(s[i+1][j],s[i+1][j-w[i]]+v[i]//是否选择这个物品,都会i加1
//完全背包递推关系:s[i+1][j]=max(s[i+1][j],s[i+1][j-k*w[i]]+v[i]);
//                  s[i+1][j-k*w[i]]+v[i](k>=0)=max(s[i+1][j-k*w[i]]+v[i]*k,s[i][j]);(k>=1)
//                  s[i+1][j-k*w[i]]+v[i]*k(k>=1)=s[i+1][j-w[i]]+v[i]
//综上:            s[i+1][j]=max(max(s[i][j],s[i+1][j-w[i]]+v[i]),s[i+1][j])
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 110
using  namespace std;
int N,W,v[maxn],w[maxn],s[maxn][10100];
int main()
{
    while(cin>>N>>W&&W)
    {
        for(int i=1;i<=N;i++)
            cin>>v[i];
        for(int i=1;i<=N;i++)
            cin>>w[i];
        memset(s,0,sizeof(s));
        for(int i=1;i<=N;i++)
        {
            for(int j=0;j<=W;j++)
            {
                if(j>=w[i])
                    s[i+1][j]=max(max(s[i+1][j-w[i]]+v[i],s[i][j]),s[i+1][j]);
                else
                    s[i+1][j]=s[i][j];
            }
        }
        cout<<s[N+1][W]<<endl;
    }
    return 0;
}

posted @ 2020-08-17 22:43  Joelin12  阅读(103)  评论(0)    收藏  举报