• 背包问题 [动态规划] (01,完全,多重,混合)


    0/1背包问题

    1.状态函数

    f [ i ] [ j ] = s f[i][j]=s f[i][j]=s:选完前面i个物品的时候,背包的体积是j,能够得到的最大的价值为s 。

    2.答案

    f [ N ] [ V ] f[N][V] f[N][V]

    3.递推起点和边界

    f [ i ] [ 0 ] = 0 , f [ 0 ] [ j ] = 0 f[i][0]=0,f[0][j]=0 f[i][0]=0,f[0][j]=0

    4、状态转移方程(递推关系)
    for(int i=1;i<=N;i++)//物品的下标
    	for(int j=1;j<=V;j++)
    		if(j<v[i])
    			f[i][j]=f[i-1][j];
    		else
    			f[i][j]=max(f[i-1][j],w[i]+f[i-1][j-v[i]]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    法一:二维数组(从前往后推)

    #include
    
    using namespace std;
    
    int N,V;
    
    int v[1000000],w[1000000];//v数组表示体积,w数组表示价值 
    
    int f[10000][10000];
    
    int main()
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    	}
    	
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=1;j<=V;j++)
    		{
    			if(j<v[i])
    			{
    				f[i][j]=f[i-1][j];
    			}
    			else
    			{
    				f[i][j]=max(f[i-1][j],w[i]+f[i-1][j-v[i]]);
    			}
    		}
    	}
    	
    	cout<<f[N][V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    法二:一维滚动数组(从前往后推)

    f [ j ] f[j] f[j]012345678910
    0013556891012
    #include
    
    using namespace std;
    
    int N,V;
    
    int v[100000],w[100000];
    
    int f[100000];
    
    int main()
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    	}
    	
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=V;j>=v[i];j--)
    		{
    			f[j]=max(f[j],f[j-v[i]]);
    		}
    	}
    	
    	cout<<f[V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    完全背包

    法一:二维数组(从前往后推)

    #include
    
    using namespace std;
    
    int V,N;
    
    int v[100000],w[100000];
    
    int f[10000][10000];
    
    int main() 
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    	}
    
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=1;j<=V;j++)
    		{
    			if(j<v[i])
    			{
    				f[i][j]=f[i-1][j];
    			}
    			else
    			{
    				f[i][j]=max(f[i-1][j],w[i]+f[i][j-v[i]]);
    			}
    		}
    	}
    	
    	cout<<"max="<<f[N][V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    法二:一维滚动数组(从前往后推)

    #include
    
    using namespace std;
    
    int N,V;
    
    int v[100000],w[100000];
    
    int f[100000];
    
    int main()
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    	}
    	
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=v[i];j<=V;j++)
    		{
    			f[j]=max(f[j],w[i]+f[j-v[i]]);
    		}
    	}
    	
    	cout<<"max="<<f[V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    多重背包

    法一:视为多次0/1背包(一维滚动数组从后往前推)

    #include
    
    using namespace std;
    
    int N,V;
    
    int v[100000],w[100000],s[100000];
    
    int f[100000];
    
    int main()
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    		cin>>s[i];//拥有该物品的数量 
    	}
    	
    	for(int i=1;i<=N;i++)//物品标号从1...n
    	{
    		for(int k=1;k<=s[i];k++)//第i个物品的数量,滚动s[i]次
    		{
    			for(int j=V;j>=v[i];j--)//从前往后推:s[i]次刷新,滚动,0/1背包
    			{
    				f[j]=max(f[j],w[i]+f[j-v[i]]);
    			}
    		}
    	}
    	
    	cout<<f[V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    法二:多件物品看成一个整体(一维滚动数组从后往前推)

    #include
    
    using namespace std;
    
    int N,V;
    
    int v[100000],w[100000],s[100000];
    
    int f[100000];
    
    int main()
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    		cin>>s[i];//拥有该物品的数量 
    	}
    	
    	for(int i=1;i<=N;i++)
    	{
    		for(int j=V;j>=0;j--)
    		{
    			for(int k=0;k<=s[i];k++)
    			{
    				if(k*v[i]>j)
    				{
    					break;
    				}
    				else
    				{
    					f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
    				}
    			}
    		}
    	}
    	
    	cout<<f[V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    法三:二进制方法拆分为多件物品的组合(即成为了0/1背包)

    拆分出来的每个大物品都是独一无二的(一维滚动数组从后往前推)

    #include
    
    using namespace std;
    
    int N,V;
    
    int v[100000],w[100000],s[100000];
    
    int f[100000];
    
    int n;
    
    int main()
    {
    	cin>>N>>V;
    	
    	int a,b,s;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>a>>b>>s;
    		
    		int k=1;
    		
    		while(k<s)
    		{
    			n++;
    			
    			v[n]=k*a;
    			w[n]=k*b;
    			
    			s=s-k;
    			
    			k=k*2;	
    		}	
    		
    		n++;
    		
    		v[n]=s*a;
    		w[n]=s*b;
    	}	
    	
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=V;j>=v[i];j--)
    		{
    			f[j]=max(f[j],w[i]+f[j-v[i]]);
    		}
    	}
    	
    	cout<<f[V];
    	
    	return 0; 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    混合背包

    一维滚动数组从后往前推

    #include
    
    using namespace std;
    
    int N,V;
    
    int v[100000],w[100000],s[100000];
    
    int f[100000];
    
    int main()
    {
    	cin>>V>>N;
    	
    	for(int i=1;i<=N;i++)
    	{
    		cin>>v[i];
    		cin>>w[i];
    		cin>>s[i];
    	}
    	
    	for(int i=1;i<=N;i++)
    	{
    		if(s[i]==0)
    		{
    			for(int j=v[i];j<=V;j++)//完全背包:从前往后推
    			{
    				f[j]=max(f[j],w[i]+f[j-v[i]]);//相同
    			}
    		}
    		else
    		{
    			for(int k=1;k<=s[i];k++)//多重背包就是多个0/1背包的组合
    			{
    				for(int j=V;j>=v[i];j--)//0/1背包从后往前推
    				{
    					f[j]=max(f[j],w[i]+f[j-v[i]]);//相同
    				}
    			}
    		}
    	}
    	
    	cout<<f[V];
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
  • 相关阅读:
    微服务链路追踪-SkyWalking
    Java:Swift与Java |你应该知道的最有价值的区别
    Compose Desktop 使用中的几个问题(分平台加载资源、编写Gradle 任务下载平台资源、桌面特有组件、鼠标&键盘事件)
    vue使用el-date-picker(选择日期和时间)
    如何优雅的删除HashMap元素
    2022UUCTF-web
    docker常用命令总结
    类与对象(中)
    【unity3D】Input Field组件(可供用户输入的文本框)
    Visual Studio 2022下载安装及使用教程
  • 原文地址:https://blog.csdn.net/m0_66603329/article/details/126936240