这道题看起来比较像经典的货币问题,贪心可解。其实仔细观察的会发现20-50, 200-500之间不存在倍数关系,所以没有办法直观的用贪心来解。假如在某个例子中 只剩下了一张50 三张20 此时差为60,按照贪心算法的话 你会直接拿50 还剩的10你就拿不出来了
我们这时去凑出来倍数就好了,比如放两个50就等于放一个100,也就符合了倍数关系。
注意分类讨论。
//too rich
#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef struct money{
int c; //面值
int n; //数量
}Money;
Money m[10];
typedef long long ll;
ll sum1, sum2, dif, p; //总张数,总金额,差值
int cont, T;//答案,最优张数
//返回答案,若找不到返回-1
int solve(int dif)
{
int ans = 0;
for(int i = 10; i >= 1; i--){
if(p == 0) break;
if(m[i].n <= 0) continue;
//当面额为50或500时,试试两个两个放,再试试一个一个放
if(m[i].c==50 || m[i].c==500){
//默认为两个两个放
if(m[i].n >= 2){
//核心算法,较难理解
int tn = m[i].n/2;
int t1 = min(dif, m[i].c*tn*2);
int t2 = t1/(m[i].c*2);
dif -= t2*m[i].c*2;
ans += t2*2;
}
}else{
//面额不为50,500时
int t1 = min(dif, m[i].c*m[i].n);
int t2 = t1/m[i].c;
dif -= t2*m[i].c;
ans += t2;
}
}
if(dif == 0) return ans;
return -1;
}
int main()
{
m[1].c = 1;m[2].c = 5;m[3].c = 10;m[4].c = 20;m[5].c = 50;
m[6].c = 100;m[7].c = 200;m[8].c = 500;m[9].c = 1000;m[10].c = 2000;
scanf("%d",&T);
while(T--){
sum1 = 0, sum2 = 0, cont = 1e9;
scanf("%lld",&p);
for(int i = 1; i <= 10; i++){
scanf("%d",&m[i].n);
sum1 += m[i].n;
sum2 += (m[i].c * m[i].n);
}
dif = sum2 - p; //差
if(dif < 0){
printf("-1\n");
continue;
}
//情况1:50,500都用偶数张
int ans = solve(dif);
if(ans != -1) cont = min(ans, cont);
//情况2:50用奇数张
if(dif>=50 && m[5].n>0){
m[5].n--;
int ans = solve(dif-50);
m[5].n++; //回溯
if(ans != -1) cont = min(ans+1, cont);
}
//情况3:500用奇数张
if(dif>=500 && m[8].n>0){
m[8].n--;
int ans = solve(dif-500);
m[8].n++; //回溯
if(ans != -1) cont = min(ans+1, cont);
}
//情况4:50,500都用奇数张
if(dif>=550 && m[5].n>0 && m[8].n>0){
m[5].n--; m[8].n--;
int ans = solve(dif-550);
m[5].n++; m[8].n++;//回溯
if(ans != -1) cont = min(ans+2, cont);
}
if(cont == -1) printf("-1\n");
else printf("%lld\n",sum1-cont);
}
return 0;
}