題意
就是找零錢, 給定美國貨幣25美分,10美分,5美分,1美分的硬幣個數,以及 需要找的錢,求解恰好找開的 最少硬幣的方案
input
5 9 9 9 37
0 9 9 9 37
10 10 10 0 37
1 3 0 10 30
1 3 6 10 30
0 0 0 0 0
output
Dispense 1 quarters, 1 dimes, 0 nickels, and 2 pennies.
Dispense 0 quarters, 3 dimes, 1 nickels, and 2 pennies.
Cannot dispense the desired amount.
Dispense 0 quarters, 3 dimes, 0 nickels, and 0 pennies.
Dispense 1 quarters, 0 dimes, 1 nickels, and 0 pennies.
思路
當時第一個想法就是貪心,因爲 是硬幣, 記得之前證明過 硬幣類的貪心類問題的可解性, 不幸WA了。。。 後來想了想 硬幣個數有限制,估計這裏會有問題,但是沒有找反例也懶得去翻書證明了。。
然後只能暴力了。。。
直接四重循環或者 四層dfs 都可以;這裏使用dfs
一共分四層,在k層的時候,依次使用0,1,,,num[k]個第k種硬幣,接着繼續dfs(k+1) 直到最後一層;
過程是用臨時數組保存狀態,最後判斷此種狀態可以不可以達到最終的錢數。若能,比較最值。
PS: 有一種剪枝情況: 在循環num[k]的時候,若當前總和已經超過了 總錢數,則剪掉。
代碼
/* Accepted 2273 C++ 1.0K 0'00.00" 852K */
#include <stdio.h>
int num[4],coin[4]={25,10,5,1},ans[4],tmp[4],Min,flag,money;
/*num爲各個硬幣個數,ans爲最優解,tmp保存當前狀態解,flag標誌是否有解,money爲輸入總錢數*/
void dfs(int k)
{
if(k>3)
{
int n=0,sum=0;
for(int i=0;i<4;i++)
{
n+=tmp[i];
sum+=tmp[i]*coin[i];
}
if(sum==money)
{
if(n<=Min)
{
for(int i=0;i<4;i++)
ans[i]=tmp[i];
Min=n;
}
flag=1;
}
}
else
{
for(int i=0;i<=num[k];i++)
{
tmp[k]=i;
//剪枝代碼,若當前 得到的錢數已經超過了總錢數,則不必遞歸
int sum=0;
for(int j=0;j<k;j++)
sum+=coin[j]*tmp[j];
if(sum>money) break;
//
dfs(k+1);
}
}
}
int main()
{
while(scanf("%d%d%d%d%d",&num[0],&num[1],&num[2],&num[3],&money)&&(num[0]+num[1]+num[2]+num[3]+money))
{
flag=0;
Min=num[0]+num[1]+num[2]+num[3]+1;
dfs(0);
flag?printf("Dispense %d quarters, %d dimes, %d nickels, and %d pennies.\n",ans[0],ans[1],ans[2],ans[3]):printf("Cannot dispense the desired amount.\n");
}
}