链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5527
题意:
给出硬币面值和数量,让你用数量最多的硬币凑出给定的数.
思路:
正反思考都行.
正面想就是要尽可能多的用面值少的硬币,所以从面值大的开始考虑,假设把比它面值小的都选上后,看看需要补多少个当前面值硬币.
反面就是想去掉尽量少的硬币,使得剩下的刚好满足要求.
正:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int val[11] = {0, 1, 5, 10, 20, 50, 100, 200, 500, 1000, 2000};
ll sum[11];//前缀和
int c[11];
int ans;
void dfs(int rest, int index,int cnt)//rest 剩下的钱 index 当前处理的面值下标 cnt 数量
{
if(rest < 0 ) return ;
if(index == 0)
{
if(rest == 0)
ans = max(ans,cnt);
return ;
}
ll cur = max(rest - sum[index - 1],(ll)0); //减去价格小的硬币价钱之和后可以由当前硬币提供的价钱
int curnum = cur / val[index]; //可以由当前硬币提供的数量
if(cur % val[index])//如果不能整出,那么必然要加一
{
curnum++;
}
if(curnum <= c[index])
dfs(rest - curnum * val[index], index - 1, cnt + curnum);
curnum++;
if(curnum <= c[index])
dfs(rest - curnum * val[index], index - 1, cnt + curnum);
}
int main()
{
int t,p;
cin>>t;
while(t--)
{
memset(sum,0,sizeof sum);
ans = -1;
cin>>p;
for(int i = 1; i <= 10; i++)
cin>>c[i];
for(int i = 1; i <= 10 ; i++)
{
sum[i] = sum[i-1] + val[i] * c[i];
}
dfs(p,10,0);
cout<<ans<<endl;
}
return 0;
}
反:
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
LL a[]= {0,1,5,10,20,50,100,200,500,1000,2000}, b[13];
LL ans;
void dfs(int cur, LL tot, LL cnt)
{
if(tot < 0) return;
if(cur == 0)
{
if(tot == 0)
ans = min(ans, cnt);
return;
}
LL tmp = min((LL)b[cur], tot/a[cur]);
dfs(cur-1, tot-tmp*a[cur], cnt+tmp);
if(tmp) dfs(cur-1, tot-(tmp-1)*a[cur], cnt+tmp-1);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
LL tot = 0, sum = 0, all = 0;
ans = INF;
scanf("%lld",&sum);
for(int i=1; i<=10; ++i) scanf("%lld",&b[i]), tot += a[i]*b[i], all += b[i];
if(tot < sum)
{
puts("-1");
continue;
}
dfs(10, tot-sum, 0);
if(ans == INF) puts("-1");
else
printf("%lld\n",all-ans);
}
return 0;
}