题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6685
题意:用面额分别为10,20,50,100的四种硬币组成n(n<=100)个数(wi<=1e9),求问最少需要多少硬币
输入:
The first line of the input contains a single integer T(1≤T≤500), the number of test cases.
For each test case, the first line contains a single integer n(1≤n≤100), the number of combos sold in the canteen.
The second line contains n positive integers w1,…,wn(1≤wi≤109), which represents the prices.
分析:
首先 10分的硬币多只会用一个,如果用了两个,直接替换成一个 10分一个 20分一定不亏。(两个10分表示的数是一个20一个10表示的数的子集)
20分的硬币多只会用三个,如果用了四个,直接替换成一个 10分两个20 分一个 50分一定不亏。
50分的硬币多只会用一个,如果用了两个,直接替换成一个 50分和一个1元一定不亏。
对于任何一种情况,重复使用上述规则一定会达到一个 10分硬币最多一个, 20分最多三个, 50分最多一个的情况,不会陷入重复甩锅的死循环。
因此枚举这三种硬币分别用了多少个,然后整百的部分直接用一元硬币填,取少的答案就行了。
#include <iostream> #include <cmath> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #include <map> #include <set> using namespace std; #define fi first #define se second #define mp make_pair #define pb push_back const int N=1100,lim=40; int A[N],n,ans=1e9,pd[lim+10]; const int w[4]={1,2,5,10}; const int num[4]={1,3,1,3}; void put(int k){ for (int i=lim;i>=k;i--) pd[i]|=pd[i-k]; } int calc(){ int w=0; for (int i=1;i<=n;i++){ int k=A[i]%10; int num=1e9; while (k<=lim&&k<=A[i]){ if (pd[k]) num=min(num,(A[i]-k)/10); k+=10; } w=max(w,num); } return w; } void getans(int k,int tot){ if (k==4){ ans=min(ans,calc()+tot); return; } int back[lim+10]; memcpy(back,pd,sizeof pd); for (int i=0;i<=num[k];i++){ getans(k+1,tot+i); put(w[k]); } memcpy(pd,back,sizeof back); } void solve(){ scanf("%d",&n); for (int i=1;i<=n;i++){ scanf("%d",&A[i]); } for (int i=1;i<=n;i++){ if (A[i]%10){ printf("-1 "); return; } A[i]/=10; } ans=1e9; memset(pd,0x00,sizeof pd); pd[0]=1; getans(0,0); printf("%d ",ans); } int main(){ int t; scanf("%d",&t); for (;t;t--) solve(); return 0; }