题目大意
解题思路
这道硬币问题比较特殊,因为所有的币值都是更小币值的倍数(这句话存疑,以后有时间证明试试,个人感觉是这样),因此可以使用贪心算法求解。
(1)贪心思路:
- 贪心策略: 尽量选择现有更大面值的硬币去支付还需要支付的钱。
- 复杂度: O ( n ) O(n) O(n)
但是如果换一种硬币面额,比如现在有 { 2 , 3 , 4 } \{2,3,4\} {2,3,4}, 我们要组成面额为 5 5 5的硬币。如果先选了4,就不行了。基于此,我们提出动态规划思想。我们将其转换为多重背包问题。
(2)动态规划思路:
首先我们需要采用多重背包将数量进行二进制压缩,我们使用:(1)
v
a
l
u
e
[
i
]
value[i]
value[i]表示压缩后的第
i
i
i个压缩硬币的价值.(2):
w
e
i
g
h
t
[
i
]
weight[i]
weight[i]:表示第
i
i
i个压缩硬币实际代表的硬币数。如下图所示:
基于此,我们有:
- 定义 d p [ i ] [ j ] dp[i][j] dp[i][j]: 前 i i i个硬币组成面额为 j j j的钱需要的最小硬币数量。如果为 i n f inf inf,则不能组成。
- 目标 d p [ t o t a l ] [ A ] dp[total][A] dp[total][A]: 即所有硬币组成目标面额 A A A所需的最小硬币数量。
- 状态转移: 对于第
i
i
i个压缩硬币,我们可以有选和不选两种方式。因此:
d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v a l u e [ i ] ] + w e i g h t [ i ] ) dp[i][j] = min(dp[i-1][j], dp[i-1][j-value[i]] + weight[i]) dp[i][j]=min(dp[i−1][j],dp[i−1][j−value[i]]+weight[i]) - 转移策略: 按 i i i从小到大更新。
- 初始状态: d p [ 0 ] [ ∗ ] = i n f , d p [ 0 ] [ 0 ] = 0 dp[0][*] = inf, dp[0][0] = 0 dp[0][∗]=inf,dp[0][0]=0.
- 复杂度: O ( n l o g ( C i ) A ) O(nlog(C_i)A) O(nlog(Ci)A):其中 n l o g ( C i ) nlog(C_i) nlog(Ci)表示压缩后的物品个数, A A A最大的表示价值。
- 缺点:由于这道题A太大,因此无法用背包思想求解。
代码
- 贪心策略:
#include<iostream>
using namespace std;
int main()
{
int A;
int value[6] = {1, 5, 10, 50, 100, 500};
int num[6];
for(int i=0; i<6; i++)
cin >> num[i];
cin >> A;
int ans = 0;
for(int i=5; i>=0; i--)
{
int per_num = min(num[i], A/value[i]);
ans += per_num;
A -= per_num * value[i];
if(A == 0)
break;
}
cout << ans << endl;
return 0;
}
- 动态规划: 由于最大价值 A A A太大,因此这里按照最大价值仅有 1000 1000 1000编码。
#include<iostream>
using namespace std;
const int MAX = 30*5+5;
const int MAXM = 1000;
const int inf = 1 << 20;
int value[MAX];
int weight[MAX];
int dp[2][1000];
int main()
{
int C;
int cnt = 1;
int a[6] = {1,5,10,50,100,500};
for(int i=0; i<6; i++)
{
cin >> C;
int now_num = 1;
// --------------注意多重背包的压缩方法-----------------
while((C-now_num) >= 0)
{
value[cnt] = now_num * a[i];
weight[cnt] = now_num;
cnt++;
C -= now_num;
now_num <<= 1;
}
if(C)
{
value[cnt] = C * a[i];
weight[cnt] = C;
cnt++;
}
------------------------------------------------------
}
int A;
cin >> A;
for(int i=0; i<=A; i++)
dp[0][i] = inf;
dp[0][0] = 0;
for(int i=1; i<=cnt-1; i++)
{
for(int j=0; j<=A; j++)
{
if(j >= value[i])
dp[i%2][j] = min(dp[1-i%2][j], dp[1-i%2][j-value[i]] + weight[i]);
else
dp[i%2][j] = dp[1-i%2][j];
}
}
cout << dp[1-cnt%2][A] << endl;
return 0;
}