动态规划--背包问题

1、01背包问题

将质量为wi、价值为vi的n种物品,每种物品只有一个,放入最大承重为m的背包中,求背包所能得到的最大价值。

动态转移方程:f[i][j]=max(f[i-1][j],f[i-1][j-wi]+vi);

f[i][j]的含义是从前i种物品中取物品放入最大承重为j的背包中,所能得到的最大价值。

它等于下述两种情况的最大值:

   1、忽略第i种物品,从前i-1种物品中取物品放入最大承重为j的背包中,所能得到的最大价值。

   2、直接将第i种物品放入背包中,从前i-1种物品中取物品放入最大承重为j-wi的背包中,所能得到的最大价值。

时间复杂度为O(N*M)

代码如下:

#include <iostream>
#include <math.h>
int num,m;
int v[100],w[100];
int dp[100][100];
void solve();
using namespace std;
int main(){
cin>>num>>m;

int i,j;
for(i=0;i<num;i++){
    cin>>w[i]>>v[i];
}

for(i=0;i<num;i++)dp[i][0]=0;
for(i=0;i<m;i++)dp[0][i]=0;

solve();
/*输出dp[i][j]
for(i=0;i<num+1;i++){
    for(j=0;j<=m;j++)
    cout<<dp[i][j];
        cout<<endl;
}
*/
cout<<dp[num][m]<<endl;
return 0;
}

void solve(){
int i,j;
for(i=0;i<num;i++){
    for(j=0;j<=m;j++){
        if(j<w[i])dp[i+1][j]=dp[i][j];//动态规划
        else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
    }
}
}

上述为背包承重小于等于m时所得到的最大价值。

当要求背包承重恰为m时,背包所能得到的最大价值,此时除了将dp[0][i]和dp[i][0]初始化为0外,还需将剩余dp初始化为负无穷。当dp[i][j]为负无穷时,表示无解。

空间优化:可将dp[i][j]二维数组优化为一维数组。时间复杂度为O(N*M)

for(i=0;i<num;i++)    

    for(j=n;j>0;j--)

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

实际代码:

#include <iostream>
#include <math.h>
int num,m;
int v[100],w[100];
int dp[100];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;
for(i=0;i<num;i++){
    cin>>w[i]>>v[i];
}
for(i=0;i<m;i++)dp[i]=0;
solve();
cout<<dp[m]<<endl;
return 0;
}

void solve(){
    int i,j;
    for(i=0;i<num;i++){
        for(j=m;j>=0;j--){
            if(w[i]<=j){
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
            }
            cout<<dp[j];
        }
        cout<<endl;
    }
}


2、完全背包问题

将质量为wi、价值为vi的n种物品,每种物品有无限多件,放入最大承重为m的背包中,求背包所能得到的最大价值。

动态转移方程:f[i][j]=max(max(f[i-1][j-k*w[i]]+k*v[i]|   0<=k*w[i]<=j);

状态数为n*m个,但求解每个状态的时间复杂度不再为常数,而是为o(j/w[i]),所以时间复杂度高于O(n*m);

#include <iostream>
int num,m;
int v[100],w[100];
int dp[100][100];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;

for(i=0;i<num;i++){
    cin>>w[i]>>v[i];
}

for(i=0;i<=num;i++)dp[i][0]=0;
for(i=0;i<=m;i++)dp[0][i]=0;

solve();
cout<<dp[num][m]<<endl;
return 0;
}
void solve(){

int i,j,k;
for(i=0;i<num;i++){
for(j=0;j<=m;j++){
        if(j<w[i]){
            dp[i+1][j]=dp[i][j];
        }else{
        int max1=0;
    for(k=0;k<=j/w[i];k++){
        int n=dp[i][j-k*w[i]]+k*v[i];
        if(max1<n)
            max1=n;
    }
    dp[i+1][j]=max1;

    }
     cout<<dp[i+1][j];//输出验证
}
cout<<endl;
}


}




空间优化一维数组。

状态转移方程:

for(i=0;i<num;i++)

for(j=1;j<=m;j++)

f[j]=max(f[j],f[j-w[j]]+v[j]);复杂度O(num*n)


#include <iostream>
int num,m;
int v[100],w[100];
int dp[100];

using namespace std;
int main(){
cin>>num>>m;
int i,j;

for(i=0;i<num;i++){
    cin>>w[i]>>v[i];
}

for(i=0;i<num;i++)
    for(j=1;j<=m;j++)
    if(j>=w[i])
    dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//dp[i+1][j]=max(dp[i][j],dp[i+1][j-k*w[i]]+k*v[i]);
 
cout<<dp[m];
return 0;    
}

3、多重背包问题

将质量为wi、价值为vi的n种物品,每种物品有ci件,放入最大承重为m的背包中,求背包所能得到的最大价值。

类似完全背包问题:

二维状态转移方程:f[i][j]=max(f[i-1][j-k*mi]+k*vi| 0<=k<=ci);

复杂度:O(j*(c1+c2+...+cn))

#include <iostream>
int num,m;
int v[100],w[100],c[100];
int dp[1001][1001];
void solve();
using namespace std;
int main(){
cin>>num>>m;
int i,j;

for(i=0;i<num;i++){
    cin>>w[i]>>v[i]>>c[i];
}


for(i=0;i<=num;i++)dp[i][0]=0;
for(i=0;i<=m;i++)dp[0][i]=0;

solve();
cout<<dp[num][m]<<endl;
return 0;
}
void solve(){
int i,j,k;
for(i=0;i<num;i++){
    for(j=0;j<=m;j++){
            if(w[i]>j){
                   dp[i+1][j]=dp[i][j];
            }else{
        int max1=0;
        for(k=0;k<=c[i];k++){
                if(j<k*w[i]) break;//必须能够有那么多重量空间
            int n = dp[i][j-k*w[i]]+k*v[i];
            if(max1<n)
                max1 = n;
        }
         dp[i+1][j]=max1;
    }
    cout<<dp[i+1][j]<<" ";//输出中间过程验证
    }

cout<<endl;
}
}

空间优化一维数组:

#include<stdio.h>
#include<algorithm>
using namespace std;
int w[1002], v[1002], c[1002];
int dp[1002];
int n, m;
int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; i++)
       cin<<w[i]<<v[i]<<c[i];
    for (int i = 1; i <= n; i++){
       for (int j = m; j >= 0; j--){
          for (int k = 0; k <= c[i]; k++)
          {
               if (j-k*w[i]<0) break;
               f[j] = max(f[j],f[j-k*w[i]]+k*v[i]);//f[i][j]=max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]);
          }
          printf("%d ",f[j]);
       }
       printf("\n");
    }
    printf("%d\n",f[m]);
    return 0;
}


时间复杂度优化:可通过将第i种物品的ci件进行划分,化分为1,2,4,8,...,2^(k-1),ci-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。此时将复杂度变为O(j*(log c1 + log c2 +log c3... +log cn))。

四、混合三种背包问题

for(i =0;i<num;i++)

{

if(ni为01背包问题){

for(j=N;j>=wi;j++)

dp[j]=max(dp[j],dp[j-wi]+vi);

}else if(ni为完全背包问题){

for(j=wi;j<=N;j++)

dp[j]=max(dp[j],dp[j-wi]+vi);

}else if (ni为部分背包问题){

for (int j = m; j >= 0; j--){
          for (int k = 0; k <= c[i]; k++)
          {
               if (j-k*w[i]<0) break;
               f[j] = max(f[j],f[j-k*w[i]]+k*v[i]);
          }//不是最优解 可采用O(j*(log c1 + log c2 +log c3... +log cn))

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值