刷leetcode hot100--动态规划3.13/3.14/4.6/4.15/4.19/4.20

第五章 动态规划:背包问题

背包问题:(ps:我这里的小写v一般指的是value,w一般指weight)

【0-1背包,】

N个物品,v[i],w[i],每个物品最多用几次,容量为V的背包,价值max【容量限制,价值max】

0-1背包【每件物品最多用1次】

2. 01背包问题 - AcWing题库

f(i,j) 表示总重量<=j只从前i个物品选的价值max

f(i,j) = max{含i,不含i} = max( f(i-1,j-w[i]) + v[i] , f(i-1,j) )

代码

错因:注意!!!明确i和j从多少开始遍历,对于f[i][j]要表示前i个物品,体积小于j的max价值,读取物品要从i=1开始,而不是0-->vector<int>(N+1)

如果需要初始化0,可以vector<vector<int>> f(N+1,vector<int>(V+1,0));

#include<iostream>
#include<algorithm>
using namespace std;

//  件物品的体积和价值。
int main(){
    int N,V;
    cin>>N;
    cin>>V;
    vector<int> w(N+1);//体积
    vector<int> v(N+1);//价值
    for(int i = 1;i<N+1;i++){
        cin>>w[i];
        cin>>v[i];
    }
    //f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i]]前i个物品,不超过j
    vector<vector<int>> f(N+1,vector<int>(V+1,0));//愣住了,f(n)初始化是什么
    //f(几行,vector<int>(几列))
    
    for(int i = 1;i<=N;i++){
        for(int j =0;j<=V;j++){
            // if(i== 0 || j==0){
            //     f[i][j] = 0;
            //     continue;//忘记continue了
            // }
            f[i][j] = f[i-1][j];
            if(j>=w[i]){
                f[i][j] = max(f[i][j],f[i-1][j-w[i]]+v[i]); 
            }
            
        }
    }
    cout<<f[N][V];
}

进阶:一维dp

因为f[i]仅仅与f[i-1] 有关,所以可以变成一维数组f[j] 重量<=j的max价值,

就是相当于也是遍历i,遍历j,但是不需要每一个i对应的f都存下来,因为遍历的时候前一个还没有被后一个覆盖

其中,如果直接:f[j] =  max(f[j],f[j-w[i]]+v[i]);其实比较的是f[i][j]和f[i][j-w[i]],和预期不符

因此j倒过去遍历,这个好妙!!!!!

犯了什么错:初始化f(N+1),应该是V+1!!【要对每一个变量负责】

#include<iostream>
#include<algorithm>
 using namespace std;
 
 int main(){
     int N;//物品
     int V;//背包重量
     cin>>N>>V;
     vector<int>w(N);//体积
     vector<int>v(N);//价值
     for(int i = 0;i<N;i++){
         cin>>w[i];
         cin>>v[i];
     }
     vector<int> f(V+1);
     
     // 初始化第 0 行
    for (int j = 0; j <= V; j++) {
        if (j >= w[0]) {
            f[j] = v[0];
        }
    }
    for(int i = 1;i<N;i++){//物品
        for(int j =V;j>=w[i];j--){
            //f[i][j] = f[i-1]
            f[j] =  max(f[j],f[j-w[i]]+v[i]);
                //这里如果直接:f[j] =  max(f[j],f[j-w[i]]+v[i]);
                //其实比较的是f[i][j]和f[i][j-w[i]],和预期不符

        }
    }
    cout<<f[V];
     
 }
完全背包【每件物品无限个】

3. 完全背包问题 - AcWing题库

f[i][j] 同样表示从前i个物品取,总重量<=j

递推公式【就像集合划分一样,不重不漏】:f[i][j] = max( f[i-1][j] , f[i-1][j - k*w[i]]+k*v[i])【k>=0 && k<=V/w[i]】

max(红框) = f[i,j-v] +w

递推公式升级版:f[i,j] = max(f[i-1,j] , f[i,j-w[i]]+v[i])

对比两个背包:

优化成一维:

f[j] = max( f[j] , f[j-v]+w](这次不需要倒着遍历j

小插曲:这个代码写得,都无语,对i=0的处理,要不然是没加continue,要不然是if的对 i==0的条件限制不全,一部分还是会有f[-1](见注释)

还是初始化vector<vector<int>>f(N+1,vector<int>(V+1,0))好一点

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main() {
    int N; // 物品数量
    int V; // 背包容量
    cin >> N >> V;
    vector<int> w(N); // 物品体积
    vector<int> v(N); // 物品价值
    for (int i = 0; i < N; i++) {
        cin >> w[i]>> v[i];
    }

    vector<vector<int>> f(N, vector<int>(V + 1, 0));

    // 动态规划
    //多重f[i,j] = max( f[i-1,j],f[i,j-w]+v)
    for (int i = 0; i < N; i++) {
        for (int j = 0; j <= V; j++) {
            // if(i == 0 && j>=w[0]){
            //     int num = j/w[0];
            //     f[0][j] = num*v[0];
            //     continue;
            // }else{
            //     f[0][j] = 0;
            //     continue;
            // }
            if(i == 0){
                if(j>=w[0]){
                    int num = j/w[0];
                    f[0][j] = num*v[0];
                }
                continue;
            }
            f[i][j] = f[i-1][j];//为啥在里面初始化,因为j<w[i]也要有值
            if(j>=w[i])
                f[i][j] = max(f[i][j], f[i][j -w[i]]+v[i]); // 选择 k 个当前物品
        }
    }

    cout << f[N - 1][V] << endl;

    return 0;
}

优化版:就挺无语的,遍历改成了i= 1开始,但是前面读取w和v还是从0 开始,但是输出f[v],输出的是131985?,最后一个是8emmmmm

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int N,V;
    cin>>N>>V;//空格?
    vector<int>w(N+1);
    vector<int> v(N+1);
    for(int i = 1;i<=N;i++){
        cin>>w[i]>>v[i];//每种物品都有无限件可用。
    }
    //f[i][j] = max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]) (k:0-j/w[i])
    //f[i][j-w[i]] =max(f[i-1][j-k*w[i]]+k*v[i]) (k:0-j/w[i])
    //f[i][j] = max(f[i-1][j],f[i][j-w[i]])
    vector<int>f(V+1);
    for(int i = 1;i<=N;i++){//前i个物品
        for(int j = 0;j<=V;j++){
            if(j>=w[i]){
                f[j] = max(f[j],f[j-w[i]]+v[i]);
            }
        }
    }
    cout<<f[V];
    
}
多重背包【每个物品个数不同个】 

每个物品最多拿s[i]

f[i,j] = max(f[i-1,j-k*w[i]]+k*v[i])(0<=k<=s[i])【类似完全背包】【O(NSV)】

参考完全背包:原来:O(nks)

f[i,j] = max( f[i-1,j] , f[i-1,j-w[i]] +v[i] , f[i-1,j-2*w[i]]+2*v[i] , ...f[i-1,j-s[i]*w[i]]+s[i]*v[i] )  

f[i,j-w[i]] = max        (f[i-1,j-w[i]]      , f[i-1,j-2*w[i]]+1*v[i] , ...,f[i-1,j-s[i]*w[i]]+(s[i]-1)*v[i],f[i-1,j-(s[i]+1)*w[i]]+(s[i])*v[i]

多了一项,不能直接用f[i,j] = max{ f[i,j-w[i]]+v[i] , f[i-1,j] }

优化:

1,2,4,8,16,32,...

可以表示0-2^n-1的所有数

即可以把s[i]拆分为logs[i]个组,进而转换为0-1背包问题

O(NVS)-->O(NlogS*V)【小小疑惑:原来是s,现在是logs个数去组合,那不是2^logs??】

最不缺的就是离谱的错误啊啊啊啊啊

1.count和cnt弄反,count计划是是s[i]-->0-1背包后的总物品数量,应该提前输出化,以及最好在代码块前自增,因为如果是代码块后,while循环可能结束,后面没东东了。比如最后一次while循环,这样count实际多了一个

2.判断s应该是>0而不是>=

想法:1.while/for中的自增操作尽量放在前面【如果统计的是个数】

2.及时记录变量的一般形式,比如cnt:1,2,4,8

// 第一行两个整数,N,V
// ,用空格隔开,分别表示物品种数和背包容积。

// 接下来有 N
//  行,每行三个整数 vi,wi,si
// ,用空格隔开,分别表示第 i
//  种物品的体积、价值和数量。
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int N,V;
    cin>>N>>V;
    // vector<int>w(N+1);//可以加吗
    // vector<int>v(N+1);
    // vector<int>s(N+1);
    int w[22000],v[22000];
    int a,b,s;//计算logs
    int count = 0;//count有问题
    for(int i = 1;i<=N;i++){
        cin>>a>>b>>s;
        //count有问题
        int cnt = 1;
        
        while(cnt<=s){
            count++;
            w[count] = a*cnt;//注意*count
            v[count] = b*cnt;
            s-=cnt;//cnt vs count
            cnt = cnt*2;
        }
        if(s>0){//先判断s>=0,是s>0
            count++;
            w[count] = a*s;//余下的怎么办
            v[count] = b*s;
           
        }
        
    }
    // cout<<count<<endl;
    vector<int>f(V+1,0);

    for(int i = 1;i<=count;i++){
        for(int j = V;j>0;j--){//
            if(j>=w[i]){
                f[j] = max(f[j],f[j-w[i]]+v[i]);
            }
        }
    }
    cout<<f[V];//cout写成count了
}
分组背包【n组,每组只能选一个】

f(i,j)从前i组物品里面选,总体积不超过j的max利益

f[i][j] = max (f[i-1,j] , f[i-1][j-w[i][k]]+v[i][k])遍历i,j,k

记录想法:原来有想过开int V[101]那没有用到的数组元素怎么办,其实在遍历时,范围已经卡好了,就是for中i和j的区间

注意:N<100开的是101的大小,因为自己规定的是v[1,2,3,...]

// 第一行有两个整数 N,V
// ,用空格隔开,分别表示物品组数和背包容量。

// 接下来有 N
//  组数据:

// 每组数据第一行有一个整数 Si
// ,表示第 i
//  个物品组的物品数量;
// 每组数据接下来有 Si
//  行,每行有两个整数 vij,wij
// ,用空格隔开,分别表示第 i
//  个物品组的第 j
//  个物品的体积和价值;
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    int N,V;//N组
    cin>>N>>V;
    vector<int>s(N+1);
    int v[101][101];
    int w[101][101];
    for(int i = 1;i<=N;i++){//N组
        cin>>s[i];
        for(int j =1;j<=s[i];j++){//每组s[i]个
            cin>>w[i][j]>>v[i][j];//每个w[1,2,3,组]【1,2,3个】
        }
       
    }
    vector<int>f(V+1);
    // f[i][j] = max f[i-1][j],f[i-1][j-w[i][k]]+v[k]]
    for(int i = 1;i<=N;i++){
        for(int j = V;j>=0;j--){
            for(int k = 1;k<=s[i];k++){
                if(j>=w[i][k])
                    f[j] = max (f[j],f[j-w[i][k]]+v[i][k]);
            }
        }
    }
    cout<< f[V];
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值