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

只需要对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.

你可能感兴趣的:(poj)