AcWing算法基础课----动态规划(一) 笔记 ( 背包:01、完全、多重、分组 )

动态规划

  • 常用模型
  • 背包
    • 01背包
    • 完全背包
    • 多重背包
    • 分组背包

常用模型

背包
线性dp
区间dp

重点:状态转移
出发点:

1. 状态表示(几维)例如 f(i,j)
	a.集合
		- 所有选法
		- 条件
	b.属性 Max、Min、数量
2. 状态计算(如何一步一步计算出每一步)
	集合划分
	原则:不重、不漏

dp优化:对代码或者方程进行等价变形

背包

n个物品,容量V的背包,每个物品v体积、w价值

01背包

n个物品,容量V的背包,每个物品v体积、w价值
特点:每件物品最多用一次

所有选法满足1.只从前i个物品中选2.总体积<=j的集合

答案:f(n,v)

集合划分:

f(i,j)  -> 不含i f(i-1,j)
        -> 含i  f(i-1,j-v[i]) + w[i] 

f(i,j) 集合的属性:这里指集合里面所有选法的最大值
f(i,j) = max(f(i-1,j),f(i-1,j-v[i])+w[i])

模板:

二维:
	for(int i=1;i<=n;i++){
 	   for(int j=0;j<=m;j++){
   	       f[i][j]=f[i-1][j];
 	       if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j]-v[i]+w[i]);
	    }
	}
	cout<<f[n][m]<<endl;

一维:  滚动数组
	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]);
        }
    }
    cout<<f[m]<<endl;

完全背包

n个物品,容量V的背包,每个物品v体积、w价值
特点:每件物品可以用无限次

分析:

状态表示f(i,j) 
	集合:所有只考虑前i个物品,且总体积不大于j的所有选法
	属性:Max
		1.去掉k个物品i
		2.求Max,f(i-1,j-k*v[i])
		3.再加回来k个物品i f(i-1,j-k*v[i])+k*w[i]
	动态转移方程:f(i,j)=f(i-1,j-k*v[i])+k*w[i]
状态计算 集合的划分
	

模板:

朴素版
	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-v[i]*k]+w[i]*k);
            }
        }
    }
    cout<<f[n][m]<<endl;

优化版
	for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j]=f[i-1][j];
            if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
        }
    }
    cout<<f[n][m]<<endl;

一维:
	for(int i=1;i<=n;i++){
        for(int j=v[i];j<=m;j++){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<[m]<<endl;

优化推算过程:
AcWing算法基础课----动态规划(一) 笔记 ( 背包:01、完全、多重、分组 )_第1张图片
01背包(上)与完全背包(下) 状态转移方程比较 :
AcWing算法基础课----动态规划(一) 笔记 ( 背包:01、完全、多重、分组 )_第2张图片

多重背包

n个物品,容量V的背包,每个物品v体积、w价值
特点:每个物品有si个

状态表示f[i,j]:
	集合: 所有只从前i个物品中选,并且总体积不超过j的选法
	属性:最大值
	动态转移方程:f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k); k=0,1,2,...,s[i]
状态计算

优化

二进制优化方式 O(nvs)–>O(nlogvs)

打包分组
例: s = 200
1,2,4,8,16,32,64,73

s
1,2,4,8,…,2^k,c ( c < 2 ^(k+1) )

模板:

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

二进制优化
	int cnt=0;
    for(int i=1;i<=n;i++){
        int a,b,s;
        cin>>a>>b>>s;
        int k=1;
        while(k<=s){
            cnt++;
            v[cnt]=a*k;
            w[cnt]=b*k;
            s-=k;
            k*=2;
        }
        if(s>0){
            cnt++;
            v[cnt]=a*s;
            w[cnt]=b*s;
        }
    }
    n=cnt;
    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]);
        }
    }
    cout<<f[m]<<endl;

分组背包

n个物品,容量V的背包,每个物品v体积、w价值
特点:物品有n组,有若干种物品,每一组最多选一种

状态表示f[i,j]
	集合:只从前i组物品中选,且总体积不大于j的所有选法
	属性:Max
状态计算 :集合划分

模板

	cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        for(int j=0;j<s[i];j++){
            cin>>v[i][j]>>w[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=m;j>=0;j--){
            for(int k=0;k<s[i];k++){
                if(v[i][k]<=j){
                    f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
                }
            }
        }
    }
    cout<<f[m]<<endl;

你可能感兴趣的:(算法学习笔记,AcWing,c++,c语言)