题意: 你有面值为1,5,10,50,100的钱币分别若干张;求购买某一价值的物品所花的最少和最多钱币数。
思路:
(1)最少的钱币数: 由于面值分别为1,5,10,50,100;大面值都可以由小面值凑数转化而来,故贪心策略适用,从大面值开始枚举,每次取当前能取的最大面值。
(2)最多的钱币数: 此时采用贪心策略不再成立了,因为若从小面值开始枚举,当前的最优解并不一定是全局的最优解了。(i.e. 商品价值为6,你有2张1,1张5,此时若按贪心策略,你先把1取完,还要凑4,但是你已经没有1,只剩下一张5了;但其实明明可以取1张1,1张5达到目标的),这是因为当前最优状态不能转移下去了,故可以采用DP做,也可以采用逆向思维继续用贪心,我要用出去最多,即我要剩下的最小,即对于总价值为(sum-price),求最小的钱币数,这就转化为(1)的问题了,最后总数减掉求出的剩下的最小钱币数就是用出的最大钱币数。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
int i,j;
int n,m;
int t;
ll a[10];//a[i]为每个价格对应的钱币的数量;
ll value[]={0,1,5,10,50,100};//value[i]对应各个价格;
ll price,sum;//price为商品的价值,sum为你有的总钱数;
scanf("%d",&t);
while(t--)
{
scanf("%lld",&price);
sum=0;
for(i=1;i<=5;i++)
{
scanf("%lld",&a[i]);
sum+=a[i]*value[i];
}
if(sum<price)
{
printf("-1 -1\n");
continue;
}
ll ans1,ans2;
ans1=ans2=0;
ll now;
//求使用最少钱币数时,贪心从大面值往小枚举;
now=price;
for(i=5;i>=1;i--)
{
if(now==0) break;
if(now>=value[i] && a[i]!=0)
{
ll num=(now/value[i]);
if(num>a[i])//数量不够
{
now-=a[i]*value[i];
ans1+=a[i];
}
else
{
now-=num*value[i];
ans1+=num;
}
}
}
if(now!=0)
{
printf("-1 -1\n");
continue;
}
//求使用最多钱币数时,逆向转化为自己剩最少的钱币数才能继续使用贪心策略;
//这样转化的话,问题变为对总价值为sum-price的价值求最少钱币数;
now=sum-price;
for(i=5;i>=1;i--)
{
if(now==0) break;
if(now>=value[i] && a[i]!=0)
{
ll num=(now/value[i]);
if(num>a[i])//数量不够
{
now-=a[i]*value[i];
ans2+=a[i];
}
else
{
now-=num*value[i];
ans2+=num;
}
}
}
if(now!=0)
{
printf("-1 -1\n");
continue;
}
//钱币总数减去自己保留的最少的钱币数就是最多的钱币数;
ans2=(a[1]+a[2]+a[3]+a[4]+a[5])-ans2;
printf("%lld %lld\n",ans1,ans2);
}
return 0;
}