完全背包

完全背包

有n种重量和价值分别为wi,vi的物品,从这些物品中挑选总重量不超过W的物品,求出挑选物品价值总和的最大值。在这里,每种物品可以挑选任意多件。

限制条件:1<=n<=100,1<=wi,vi<=100,1<=W<=10000。 

分析一下:dp[i+1][j]代表从前i种物品中挑选总重量不超过j时总价值的最大值。

递推关系为:dp[0][j]=0;

      dp[i+1][j]=max{dp[i][j-k*w[i]]+k*v[i] | 0<=k}

可以写出程序如下

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 using namespace std;
 5 const int max_n=101;
 6 int w[max_n+1];
 7 int v[max_n+1];
 8 int n,r,W;
 9 int main()
10 {
11     cin>>n;
12     for(int i=0;i<n;i++)
13     {
14         cin>>w[i]>>v[i];
15     }
16     cin>>W;
17     int dp[n+1][W+1];
18     memset(dp,0,sizeof(dp));
19     for(int i=0;i<n;i++)
20     {
21         for(int j=0;j<=W;j++)
22         {
23             for(int k=0;k*w[i]<=j;k++)
24             {
25                 dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
26             }
27         }
28     }
29     cout<<dp[n][W]<<endl;
30     return 0;
31 }
View Code

关于k的循环最坏可能从0循环到W,所以这个算法的复杂度为O(nW*W)

可以做变形:max{dp[i][j-k*w[i]]+k*v[i] | 0<=k}

     =max(dp[i][j],max{dp[i][j-k*w[i]]+k*v[i] | 1<=k})

       =max(dp[i][j],max{dp[i][(j-w[i])-k*w[i]]+k*v[i] | 0<=k}+v[i])

       =max(dp[i][j],dp[i+1][j-w[i]]+v[i])

所以递推关系式

        dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])

我们和01背包的递推关系式比较一下

        dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])

复杂度降为O(nW)

程序如下

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 using namespace std;
 5 const int max_n=101;
 6 int w[max_n+1];
 7 int v[max_n+1];
 8 int n,r,W;
 9 int main()
10 {
11     cin>>n;
12     for(int i=0;i<n;i++)
13     {
14         cin>>w[i]>>v[i];
15     }
16     cin>>W;
17     int dp[n+1][W+1];
18     memset(dp,0,sizeof(dp));
19     for(int i=0;i<n;i++)
20     {
21         for(int j=0;j<=W;j++)
22         {
23             if(j>=w[i])
24             {
25                 dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
26             }
27             else
28             {
29                 dp[i+1][j]=dp[i][j];
30             }
31         }
32     }
33     cout<<dp[n][W]<<endl;
34     return 0;
35 }
View Code

也可以通过过重复利用一个数组来实现dp。这个比较不好理解一些。

看01背包的情况

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 using namespace std;
 5 const int max_n=101;
 6 int w[max_n+1];
 7 int v[max_n+1];
 8 int n,r,W;
 9 int main()
10 {
11     cin>>n;
12     for(int i=0;i<n;i++)
13     {
14         cin>>w[i]>>v[i];
15     }
16     cin>>W;
17     int dp[W+1];
18     memset(dp,0,sizeof(dp));
19     for(int i=0;i<n;i++)
20     {
21         for(int j=W;j>=w[i];j--)
22         {
23             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
24         }
25     }
26     cout<<dp[W]<<endl;
27     return 0;
28 }
View Code

看完全背包的情况

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<memory.h>
 4 using namespace std;
 5 const int max_n=101;
 6 int w[max_n+1];
 7 int v[max_n+1];
 8 int n,r,W;
 9 int main()
10 {
11     cin>>n;
12     for(int i=0;i<n;i++)
13     {
14         cin>>w[i]>>v[i];
15     }
16     cin>>W;
17     int dp[W+1];
18     memset(dp,0,sizeof(dp));
19     for(int i=0;i<n;i++)
20     {
21         for(int j=w[i];j<=W;j++)
22         {
23             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
24         }
25     }
26     cout<<dp[W]<<endl;
27     return 0;
28 }
View Code

两者的差异只有循环的方向

原文地址:https://www.cnblogs.com/wangkaipeng/p/6429513.html