背包——多重背包(hdu2191)

首先,如果对01背包不理解的同学请移步  http://blog.csdn.net/sm9sun/article/details/53235986

理解了01背包之后,我们来说明一下多重背包,多重背包就是一个物品有m个,即对于单个物品来说,他的取舍

不再是0,1.而可能是0,1,2,3,4……那么问题看起来就复杂的多。

我们看一下01背包的循环

for(i=1;i<=n;i++)    //外层循环物品个数
    {
        for(j=0;j<=v;j++) //内层循环每个容量点
        {
            if(j-b[i]>=0)
            dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]]+a[i]);    //容量大于该物品体积时状态转移方程
            else 
            dp[i][j]=dp[i-1][j];          //容量小于该物品体积,只能舍弃

        }
  }

那么想要实现单个物品n[i]多种选择的话,首先必须要在外层循环里再加一层循环,来遍历所取物品的个数

例如:

for(i=1;i<=n;i++)    //外层循环物品个数
    {

      for(k=0;k<=[c][i];k++)    //假设C[i]是物品i的数量

      {

        for(j=0;j<=v;j++) //内层循环每个容量点
        {
            if(j-b[i]*k>=0)
            dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]*k]+a[i]*k);    //取k个物品
            else 
            dp[i][j]=dp[i-1][j];          //容量小于该物品体积,只能舍弃
        }

     }
  }

假设 大概是这样的,应该是能够实现~并且也不用考虑时间复杂度,因为c[i]是根据不同物品个数而定,我们仔细分析一下,发现i循环,k循环是在干一件事,

其实就是遍历物品,只不过k进一步的说明这个物品取舍情况。如果单个物品i的k值很大,比如10000,那么也就在当前i值走10000次循环而已,当i改变后,下一次k循环

可能又只是走1次,2次。

那么我们完全可以换一种更简单的思路,既然我还是遍历了所有物品,我们不如将其合并。如果i物品有k个,那么我就索性认为有k个物品,其体积、价值完全等价与i

这样就等同于01背包问题了。

假设物品i有k个,其体积为v,价值为p,即

while(i.k--)
{
    count++;
    V[count]=i.v;    
    P[count]=i.p;
}


题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2191


题目描述:每袋大米都含有重量、价值、袋数,即多重背包问题。


#include<stdio.h>
int fmax(int a,int b)
{
  return a>b?a:b;
}

int dp[2100][110];
int main()
{

	int i,j,c,v,n;
	int a[2100],b[2100];
	int x,y,z,count;
	
	
	scanf("%d",&c);
	while(c--)
	{
		count=0;
		
		scanf("%d%d",&v,&n);
		while(n--)                           //把多重背包分开存 
		{
			scanf("%d%d%d",&x,&y,&z);
			while(z--)
			{
				count++;
				b[count]=x;
				a[count]=y;
				
			}

		}
		
		for(i=1;i<=count;i++)
		{
			for(j=0;j<=v;j++)
			{
				if(j>=b[i])
				dp[i][j]=fmax(dp[i-1][j],dp[i-1][j-b[i]]+a[i]);
				else
				dp[i][j]=dp[i-1][j];
			}
		}
/*		
		for(i=0;i<=count;i++)
		{
			for(j=0;j<=v;j++)
			{
				printf("%6d ",dp[i][j]);
			}
			printf("\n");
		}
*/
		printf("%d\n",dp[count][v]);
		
		
		
		
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值