POJ3260 The Fewest Coins ——完全背包+多重背包——Pku3260

本文详细阐述了使用多重背包算法处理John的付款数问题,以及使用完全背包算法解决shopkeeper的找零钱数问题。通过证明最大可能的付款数不超过特定值,确保了算法的有效性。通过代码实现,展示了如何通过动态规划解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

只需要对John的付款数做一次多重背包,对shopkeeper的找零钱数做一次完全背包即可。

最重要的是上界的处理。可以注意到,John的付款数最多为maxv*maxv+m,也就是24400元。同理,shopkeeper找钱最多的数目为maxv*maxv.

证明如下:

如果John的付款数大于了maxv*maxv+m,即付硬币的数目大于了maxv,根据鸽笼原理,至少有两个的和对maxv取模的值相等,也就是说,这部分硬币能够用更少的maxv来代替。证毕。

 

代码:

Program Fewcoins;//By_Thispoet
Const 
	maxn=120;
	maxm=200000;
Var
	i,j,k,m,n,maxi,gone,ans		:Longint;
	f,g							:Array[0..maxm]of Longint;
	c,v							:Array[0..maxn]of Longint;
	
Function Min(i,j:Longint):Longint;
begin
	if i<j then exit(i);exit(j);
end;
	
	
BEGIN

	
	readln(n,m);
	for i:=1 to n do 
		begin
			read(v[i]);
			if v[i]>maxi then maxi:=v[i];
		end;
	fillchar(f,sizeof(f),1);
	f[0]:=0;gone:=0;
	for i:=1 to n do read(c[i]);
	maxi:=m+maxi*maxi;
	
	for i:=1 to n do
		begin
			k:=1;
			while k<=(c[i]>>1) do
				begin
					for j:=gone downto 0 do 
						if (j+v[i]*k<=maxi)and(f[j]+k<f[j+v[i]*k]) then 
							begin
								f[j+v[i]*k]:=f[j]+k;
								if (j+v[i]*k>gone) then gone:=Min(j+v[i]*k,maxi);
							end;
					k:=k<<1;
				end;
			k:=c[i]-(k>>1);
			for j:=gone downto 0 do 
				if (j+v[i]*k<=maxi)and(f[j]+k<f[j+v[i]*k]) then 
					begin
						f[j+v[i]*k]:=f[j]+k;
						if (j+v[i]*k>gone) then gone:=Min(j+v[i]*k,maxi);
					end;
		end;
		
	dec(maxi,m);
	fillchar(g,sizeof(g),1);
	g[0]:=0;
	for i:=1 to n do 
		begin
			k:=1;
			while k<=(maxi div v[i])>>1 do 
				begin
					for j:=maxi downto 0 do 
						if (j+v[i]*k<=maxi)and(g[j]+k<g[j+v[i]*k]) then g[j+v[i]*k]:=g[j]+k;
					k:=k<<1;
				end;
			k:=(maxi div v[i])-(k>>1);
			for j:=maxi downto 0 do
				if (j+v[i]*k<=maxi)and(g[j]+k<g[j+v[i]*k]) then g[j+v[i]*k]:=g[j]+k;
		end;
		
	ans:=maxlongint;
	for i:=maxi+m downto m do
		ans:=Min(f[i]+g[i-m],ans);
	
	if ans<100000 then writeln(ans) else writeln(-1);
	

END.

转载于:https://www.cnblogs.com/Thispoet/archive/2011/09/20/2182022.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值