C 背包之完全背包及多重背包

1.有一个储蓄罐,告知其空时的重量和当前重量,并给定一些钱币的价值和相应重量
求储蓄罐中最少有多少现金
输入 第一行T 表示后面有T行测试用例
第二行两个数字 空储蓄罐重量 当前储蓄罐重量
第三行一个 整形N 其后有N行,每行有两个数字钱币价值和重量
3
10 110
2
1 1
30 50
10 110
2
1 1
50 30
1 6
2
10 3
20 4
输出
60
100
this is impossible

完全背包特点 每个物品可选数量为无穷,其解法与0-1背包基本一致,不同的仅为状态更新时的顺序
0-1背包之所以逆序是为了保证更新dpj时,dp[j-list[i].w]的状态尚未因本次更新而改变,这是因为0-1
背包至多被选择一次。而完全背包可以选择无限次,那么状态dp[i][j]加号可以由可能已经放入物品i的状态

转移而来,故为顺序 

#include<stdio.h>
#define INF 0x7fffffff
int min(int a,int b)
{
	return a<b ? a:b;
} 
struct E
{
	int w;//重量 
	int v;//价值 
} list[501];
int dp[10001];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int s,tmp;
		scanf("%d%d",&tmp,&s);//空储蓄罐数量和装满储蓄罐数量 
		s-=tmp;//计算钱币所占重量 
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&list[i].v,&list[i].w);
		}
		for(int i=0;i<=s;i++)
		{
			dp[i]=INF;
		}
		dp[0]=0; 
		for(int i=1;i<=n;i++)
		{//遍历所有物品 
			for(int j=list[i].w;j<=s;j++)
			{//完全背包,顺序遍历所有可能转移状态 
				if(dp[j-list[i].w]!=INF)
				{//若不为无穷,则可以由此状态转移而来 
					dp[j]=min(dp[j],dp[j-list[i].w]+list[i].v);//取转移最小值 
				}
			}
		}
		if(dp[s]!=INF)//若存在一种方案使背包恰好装满,输出最小值 
		   printf("%d\n",dp[s]);
		else
		   puts("this is impossible");
	}
	return 0;
}
2.有资金n元,市场有m种大米,每种大米均为袋装,价格不等,只能整袋购买。
问用有限的资金最多采购多少公斤粮食
输入 正整数C 表示有C组测试数据 每组测试数据第一行两个整数n和m
分别为经费金额和大米的种类,然后是m行数据,每行包含大米的价格 重量 和对应的袋数
1
8 2
2 100 4
4 100 2
输出 购买大米的最多重量,可以假设经费买不光所有大米,且也可以不用完 
400 

多重背包 有容积V的背包,给定一些物品,包含体积w 价值v和数量k求背包最大价值量
将原数量为k的物品拆分成若干组,每组物品看作一个物品,其价值和重量为该组中所有

物品的价值重量和 

#include<stdio.h>
 int max(int a,int b)
{
	return a>b ? a:b;
} 
struct E
{
	int w;//价格 
	int v;//重量 
} list[2001];
int dp[101];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int s,n;
		scanf("%d%d",&s,&n);
		int cnt=0;//拆分后物品总数 
		for(int i=1;i<=n;i++)
		{
			int v,w,k;
			scanf("%d%d%d",&w,&v,&k);
			int c=1;
			while(k-c>0)
			{//对输入的数字k,差分成1,2,4...k-2^c+1,其中c为使最后一项大于0的整数 
				k-=c;
				list[++cnt].w=c*w;
				list[cnt].v=c*v;//拆分后的大米重量和价格均为组成该物品的大米的重量价格和 
				c*=2;
			}
			list[++cnt].w=w*k;
			list[cnt].v=v*k;
		}
		for(int i=1;i<=s;i++) dp[i]=0;
		for(int i=1;i<=cnt;i++)
		{//对拆分后的所有物品进行0-1背包 
			for(int j=s;j>=list[i].w;j--)
			{
				dp[j]=max(dp[j],dp[j-list[i].w]+list[i].v);
			}
		}
		printf("%d\n",dp[s]);
	} 
	return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值