original link - http://acm.hdu.edu.cn/showproblem.php?pid=6685
题意:
有4种硬币:10,20,50,10010,20,50,10010,20,50,100,每种无限个,现在你要挑选最少的个数,使得可以组成给出的n个价格。
解析:
开始想到超过100的应该都是直接100比较少。所以模了10到90,发现只需要10,20,20,5010,20,20,5010,20,20,50这4个就可以组成任意了,所以就将所有价格模100后塞到set中,做了一个背包求最少的选择方案。
但是后来暴力对拍才发现,90和110的情况比较特殊,110可以用20,20,20,5020,20,20,5020,20,20,50组成。所以特判一下就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int maxn=209;
int a[maxn];
int main(){
int t;scanf("%d",&t);
while(t--){
int n;scanf("%d",&n);
int _100=0;
unordered_set<int>S;
int b=0;
rep(i,1,n){
scanf("%d",a+i);
int tmp=a[i]%100;
if(tmp%10){
b=1;
}
S.insert(tmp);
}
if(b){
printf("-1\n");
continue;
}
int val[9]={1,1,1,1,2,2,2,2,5};
int mi=100;
int en=(1<<9)-1;
rep(i,0,en){
int dp[10];
memset(dp,0,sizeof dp);
dp[0]=1;
rep(j,0,8){
per(siz,8,0){
if(dp[siz]==0||siz+val[j]>9)continue;
if(i&(1<<j)){
dp[siz+val[j]]=1;
}
}
}
int can=1;
for(auto P:S){
if(!dp[P/10]){can=0;break;}
}
if(can)mi=min(mi,__builtin_popcount(i));
}
rep(i,1,n){
if(a[i]%100==0&&mi==4){
_100=max(_100,a[i]/100-1);
}
else{
_100=max(_100,a[i]/100);
}
}
int ans2=0;
int inf=1e9;
rep(i,1,n){
if(a[i]==10)ans2=1e9;
else if(a[i]%100==10){
ans2=max(ans2,4+(a[i]-110)/100);
}
if(a[i]%100==20){
ans2=max(ans2,4+(a[i]-20)/100);
}
if(a[i]%100==30){
ans2=max(ans2,inf);
}
if(a[i]%100==40){
ans2=max(ans2,4+(a[i]-40)/100);
}
if(a[i]%100==50){
ans2=max(ans2,4+(a[i]-50)/100);
}
if(a[i]%100==60){
ans2=max(ans2,4+(a[i]-60)/100);
}
if(a[i]%100==70){
ans2=max(ans2,4+(a[i]-70)/100);
}
if(a[i]%100==80){
ans2=max(ans2,inf);
}
if(a[i]%100==90){
ans2=max(ans2,4+(a[i]-90)/100);
}
if(a[i]%100==0){
ans2=max(ans2,4+a[i]/100);
}
}
printf("%d\n",min(ans2,_100+mi));
}
}