• 背包问题 [动态规划] (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
  • 相关阅读:
    HTML+CSS大作业:众志成城 抗击疫情 抗击疫情网页制作作业 疫情防控网页设计
    手机呼叫转移怎么设置
    菜鸟学习第一天
    【mongo 系列】常用操作实际操练
    ROS三种通信方式之服务通信
    扫码挪车小程序源码专业版上线了
    Maven3种打包方式之一maven-assembly-plugin的使用
    HTML5的新特性有哪些?
    金蝶云星空简单账表动态列名汇总
    把请求头信息添加到请求报文中,然后发送请求到淘宝,显示回复信息
  • 原文地址:https://blog.csdn.net/m0_66603329/article/details/126936240