蓝桥杯java算法学习(四)

注意:以下部分内容摘自Acwing,仅用于个人学习记录,不做任何商业用途。

(1)01背包问题

原链接:AcWing 2. 01背包问题(状态转移方程讲解) - AcWing (讲得很好,我搬运过来是为了自己学习用,不做任何商业用途噢~)

题目描述: 有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。

 

代码:

for(int i = 1; i <= n; i++) 
    for(int j = m; j >= 0; j--)
    {
        if(j < v[i]) 
            f[i][j] = f[i - 1][j];  // 优化前
            f[j] = f[j];            // 优化后,该行自动成立,可省略。
        else    
            f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);  // 优化前
            f[j] = max(f[j], f[j - v[i]] + w[i]);                   // 优化后
    }    

作者:深蓝
链接:https://www.acwing.com/solution/content/1374/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 实际上,只有当枚举的背包容量 >= v[i] 时才会更新状态,因此我们可以修改循环终止条件进一步优化。

for(int i = 1; i <= n; i++)
{
    for(int j = m; j >= v[i]; j--)  
        f[j] = max(f[j], f[j - v[i]] + w[i]);
} 

(2)完全背包问题 

与01背包问题的区别:每样物品可以选无数件:

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ )
        cin >> v[i] >> w[i];
    for(int i = 0; i <= m; i++)//初始化
    {
        f[0][i] = 0;
    }
    for(int i = 1; i <= n; i ++ )
        for(int j = 0; j <= m; j ++ )
            for(int k = 0; k * v[i] <= j; k ++ )
                f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
                //求出每一个 f[i][j]
    cout << f[n][m] << endl;

 优化:

转自acwing:AcWing 3. 完全背包问题 - AcWing

(3) 多重背包问题

问题描述:第 i 种物品最多有 si  件,其他同上。
传统方法: 

for(int i=1;i<=n;i++){
         for(int j=0;j<=m;j++)
             for(int k=0;k<=s[i]&&k*v[i]<=j;k++){
                 f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+k*w[i]);
             }
    }

创新:把s个相同物品拆成s个不同的,转换成01背包

当数据过大时,我们需要使用二进制优化
 如果个数为10,那么可以用1+2+4+ 3来表示,这样10个物品就可以被分成4个物品了,这样的话就是logN的复杂度,优化了很多。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
       static final int N=200002;
       //si * 每个si能拆出来的个数 + 左右预留的哨兵位置 = 2000 * log2(2000) + 2 <= 22002
       
       static int[] v =new int[N];//体积
       static int[] w =new int[N];//价值
       static int[] f =new int[N];//f存储最大价值
       
       public static void main(String[] args) throws IOException {
    	   BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
    	   int n,V;
    	   String[] strings=br.readLine().split(" ");
     	   n=Integer.parseInt(strings[0]);
     	   V=Integer.parseInt(strings[1]);
     	   int idx=1;
     	   for(int i=0;i<n;i++) {
     		   int si,vi,wi;
     		   strings=br.readLine().split(" ");
     		   vi=Integer.parseInt(strings[0]);
     		   wi=Integer.parseInt(strings[1]);
     		   si=Integer.parseInt(strings[2]);
     		   int k=1;
     		   while(si>k) {
     			   v[idx]=vi*k;
     			   w[idx]=wi*k;
     			   si-=k;
     			   k*=2;
     			   idx++;
     		   }
     		   if(si>0) {
     			   v[idx]=si*vi;
     			   w[idx]=si*wi;
     			   idx++;
     		   }
     	   }
     	   for(int i=1;i<idx;i++) {
     		   for(int m=V;m>=v[i];m--)
     			   f[m]=Math.max(f[m], f[m-v[i]]+w[i]);
     	   }
     	   System.out.print(f[V]);
       }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值