背包九讲(仅提供代码回忆)

题集传送门(1~12)

(壹)01背包

有N个物品和一个体积为V的背包,每个物品体积为v[i],价值为w[i],问背包可收纳的最大价值。

时间复杂度O(N*V)。

 1 /*
 2 f[m]即为答案。
 3 如果要表示恰好去体积为V的物品,
 4 可以把除f[0]外全置为负无穷,
 5 以保证是f[m]从f[0]推来的。
 6 */
 7 #include<iostream>
 8 using namespace std;
 9 int N,V;
10 int v[1010],w[1010],f[1010];
11 int main()
12 {
13     cin>>N>>V;
14     for(int i=0;i<N;i++)
15         cin>>v[i]>>w[i];
16     for(int i=0;i<N;i++)
17         for(int j=V;j>=v[i];j--)
18             f[j]=max(f[j],f[j-v[i]]+w[i]);
19     cout<<f[V]<<endl;
20     return 0;
21 }
View Code

 

(贰)完全背包

有N种物品和一个体积为V的背包,每种物品体积为v[i],价值为w[i],且每种物品有无限个,问背包可收纳的最大价值。

时间复杂度O(N*V)。

 1 /*
 2 f[m]即为答案。
 3 如果要表示恰好去体积为V的物品,
 4 可以把除f[0]外全置为负无穷,
 5 以保证是f[m]从f[0]推来的。
 6 */
 7 #include<iostream>
 8 using namespace std;
 9 int N,V;
10 int v,w,f[1010];
11 int main()
12 {
13     cin>>N>>V;
14     for(int i=0;i<N;i++)
15     {
16         cin>>v>>w;
17         for(int j=v;j<=V;j++)
18             f[j]=max(f[j],f[j-v]+w);
19     }
20     cout<<f[V]<<endl;
21     return 0;
22 }
View Code

  

(叁)多重背包

有N种物品和一个V体积背包,每种物品体积为v[i],价值为w[i],个数为s[i],问背包可收纳的最大价值。思路是将其01背包化。

  1. 基本解法,时间复杂度O(N*∑si)。
     1 #include<iostream>
     2 using namespace std;
     3 int N,V;
     4 int v,w,s,f[1010];
     5 int main()
     6 {
     7     cin>>N>>V;
     8     for(int i=0;i<N;i++)
     9     {
    10         cin>>v>>w>>s;
    11         for(int j=V;j>=v;j--)
    12             for(int k=1; k<=s && k*v<=j ;k++)
    13             f[j]=max(f[j],f[j-k*v]+k*w);
    14     }
    15     cout<<f[V]<<endl;
    16     return 0;
    17 }
    View Code

     

  2. 二进制优化,时间复杂度O(N*∑ log s[i])。
     1 #include<iostream>
     2 using namespace std;
     3 int N,V;
     4 int f[2020];
     5 int v[11010],w[11010];
     6 int cnt=0;
     7 //2的11次方大于2000,故开11倍
     8 int main()
     9 {
    10     int x,y,z;
    11     cin>>N>>V;
    12     for(int i=0;i<N;i++)
    13     {
    14         cin>>x>>y>>z;
    15         for(int j=1;j<=z;j<<=1)
    16         {
    17             v[cnt]=j*x;
    18             w[cnt++]=j*y;
    19             z-=j;
    20         }
    21         if(y)
    22         {
    23             v[cnt]=z*x;
    24             w[cnt++]=z*y;
    25         }
    26     }
    27     for(int i=0;i<cnt;i++)
    28         for(int j=V;j>=v[i];j--)
    29             f[j]=max(f[j],f[j-v[i]]+w[i]);
    30     cout<<f[V]<<endl;
    31     return 0;
    32 }
    View Code

      

  3. 优先队列优化,时间复杂度O(NV)。

具体解释及代码:https://blog.csdn.net/jack_jxnu/article/details/100119883

(肆)混合背包

以上三种背包问题的集合体,对此我们可以将其01和完全化,再分别处理。

#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,V,x,y,z;
int f[1010];
struct node
{
    int kind;
    int v,w;
};
vector<node> Thing;
int main()
{
    cin>>n>>V;
    for(int i=0;i<n;i++)
    {
        cin>>x>>y>>z;
        if(z>0)
        {
            for(int k=1;k<=z;k<<=1)
            {
                Thing.push_back({-1,k*x,k*y});
                z-=k;
            }
            Thing.push_back({-1,z*x,z*y});
        }
        else Thing.push_back({z,x,y});
    }
    for(auto node:Thing)
    {
        if(node.kind==-1) //01背包
        {
            for(int j=V;j>=node.v;j--)
                f[j]=max(f[j],f[j-node.v]+node.w);
        }
        else //完全背包
        {
            for(int j=node.v;j<=V;j++)
                f[j]=max(f[j],f[j-node.v]+node.w);
        }
    }
    cout<<f[V]<<endl;
    return 0;
}

  

(伍)二维费用背包

有N件物品与一个体积为V、承重为M的背包,每件物品给出体积vi、重量mi和价值wi。求背包可容纳的最大价值。

#include<cstring>
#include<iostream>
using namespace std;
int f[110][110]; //体积为i承重为j的背包的最大容纳价值
int n,V,M;
int v,w,m;
int main()
{
    cin>>n>>V>>M;
    for(int k=0;k<n;k++)
    {
        cin>>v>>m>>w;
        for(int i=V;i>=v;i--)
            for(int j=M;j>=m;j--)
                f[i][j]=max(f[i][j],f[i-v][j-m]+w);
    }
    cout<<f[V][M]<<endl;
    return 0;
}

(陆)分组背包

有n个物品组,每组物品只能选其一,问背包可以放下的最大价值。

 1 #include<cstring>
 2 #include<iostream>
 3 using namespace std;
 4 int num,w[110],v[110];
 5 int f[110],n,V;
 6 int main()
 7 {
 8     cin>>n>>V;
 9     for(int i=0;i<n;i++)
10     {
11         cin>>num;
12         for(int j=0;j<num;j++) cin>>v[j]>>w[j];
13         for(int j=V;j>=0;j--)
14             for(int k=0;k<num;k++)
15                 if(j-v[k]>=0) f[j]=max(f[j],f[j-v[k]]+w[k]);
16     }
17     cout<<f[V]<<endl;
18     return 0;
19 }
View Code

 

(柒)依赖背包

有n个物品,彼此间有树形依赖关系(选子必选父),求最大价值。

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int N=110;
 5 int head[N],edge[N],Nex[N],idx;
 6 int v[N],w[N];
 7 int f[N][N];  //表示选i及其子树,体积为j的最大价值
 8 int n,V;
 9 
10 void add(int x,int y) //加边x->y
11 {
12     edge[idx]=y;
13     Nex[idx]=head[x];
14     head[x]=idx++;
15 }
16 void dfs(int x)
17 {
18     for(int i=head[x];i!=-1;i=Nex[i])
19     {
20         int y=edge[i];
21         dfs(y);
22         for(int j=V-v[x];j>=0;j--)
23             for(int k=0;k<=j;k++)
24                 f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);
25     }
26 
27     //考虑到如果体积无法放下x节点,那么就无法放下任何东西,则为0
28     for(int i=V;i>=0;i--)
29         if(i<v[x]) f[x][i]=0;
30         else f[x][i]=f[x][i-v[x]]+w[x];  //注意不要max
31 }
32 int main()
33 {
34     memset(head,-1,sizeof head);
35     cin>>n>>V;
36     int rt,p;
37     for(int i=1;i<=n;i++)
38     {
39         cin>>v[i]>>w[i]>>p;
40         if(p==-1) rt=i;
41         else add(p,i);
42     }
43     dfs(rt);
44     cout<<f[rt][V]<<endl;
45     return 0;
46 }
View Code

 

(捌)背包问题方案数

问最优方案的解法有多少种?

 1 #include<bits/stdc++.h>
 2 #define inf 0x3f3f3f3f
 3 #define M 1000000007
 4 using namespace std;
 5 const int N=1010;
 6 int f[N],g[N];
 7 int n,V;
 8 int main()
 9 {
10     for(int i=1;i<N;i++) f[i]=-inf;
11     f[0]=0;g[0]=1;   
12     //以上初始化保证f[i]表示体积恰好为i时的最大价值
13     
14     cin>>n>>V;
15     for(int i=0;i<n;i++)
16     {
17         int v,w;
18         cin>>v>>w;
19         for(int j=V;j>=v;j--)
20         {
21             int sum=0,t=max(f[j],f[j-v]+w);
22             if(t==f[j]) sum+=g[j];
23             if(t==f[j-v]+w) sum+=g[j-v];
24             f[j]=t;
25             g[j]=sum%M;
26         }
27     }
28     
29     int maxw=0,ans=0;
30     for(int i=0;i<=V;i++) maxw=max(maxw,f[i]); //求所有体积下的可得最大价值
31     for(int i=0;i<=V;i++)
32         if(f[i]==maxw) ans=(ans+g[i])%M;
33     cout<<ans<<endl;
34     return 0;
35 }
View Code

 

(玖)求背包问题方案

要求一个放置方案使得背包容纳价值最小。可以直接逆推回去。

 1 #include<bits/stdc++.h>
 2 #define inf 0x3f3f3f3f
 3 using namespace std;
 4 const int N=1010;
 5 int v[N],w[N];
 6 int f[N][N];
 7 int n,V;
 8 int main()
 9 {
10     cin>>n>>V;
11     for(int i=1;i<=n;i++)
12         cin>>v[i]>>w[i];
13 
14     for(int i=n;i>0;i--)
15         for(int j=0;j<=V;j++)
16             if(j>=v[i]) f[i][j]=max(f[i+1][j],f[i+1][j-v[i]]+w[i]);
17             else f[i][j]=f[i+1][j];
18 
19     for(int i=1;i<=n;i++)
20         if(f[i][V]==f[i+1][V-v[i]]+w[i] && v[i]<=V)   //当心越界
21         {
22             V-=v[i];
23             cout<<i<<' ';
24         }
25     return 0;
26 }
View Code

 

posted @ 2019-08-22 17:19  然墨  阅读(221)  评论(0编辑  收藏  举报