Coins Exchange 动态规划入门题目
题意:换零钱,求找开某个面值至少需要多少枚零钱
例如:有面值1,4,6的硬币无限枚,现需要找开面值8,有两种方案,1+1+6和4+4,则至少需要两枚零钱可找开8元面额。
题解:
(一)使用N^2的动态规划,略优于深度优先遍历
用opt[N]表示换零N面值至少需要的枚数,data[k]数组存放给定的k种硬币面额。
首先将opt[data[i]]全部置1,即至少需要1枚硬币可找零。其余面额都初始化成最大值INF。
从小到大,对面值逐个进行搜索,搜索面值N,从 1+(N-1) 搜索到 N/2+N/2。
状态转移方程
for (int i = min; i <= Max; ++i) for (int j = min; j <= i; ++j) opt[i] = min(opt[j] + opt[i - j], opt[i]);
每次计算获得的新值可以更新更大的面值,符合动态规划所需的最优子结构和无后效性。
但是当N很大,k很小时,N^2的算法就会显得效率很低,例如仅有面值为1的硬币,拼凑10000面值,
10000 = 9999 + 1 = 9998 + 2 = 9997 + 3
以上其实是同一种方案。
(二)考虑状态之间的转换关系
对仅有一种零钱来讲,从之前所有状态只有一种方法能到达新状态,那就是添加一枚硬币。
即对9999添加一枚硬币得到10000,对于9998添加一枚,再添加一枚其实是重复劳动。
故新状态与之前状态的联系是添加一枚硬币。
这样能省去大量重复运算,复杂度为N*k。
//Coin Exchange #include <stdio.h> int T, N, Max, min; int data[15], opt[64005]; int main(void){ scanf("%d", &T); for (int test_case = 1; test_case <= T; ++test_case){ for (int i = 0; i < 64005; ++i)opt[i] = 64005; min = 64005; scanf("%d", &N); for (int i = 0; i < N; ++i){ scanf("%d", &data[i]); opt[data[i]] = 1; if (data[i] < min)min = data[i]; } scanf("%d", &Max); for (int i = min + 1; i <= Max; ++i) for (int j = 0; j < N; ++j){ if ((data[j]<i) && (opt[i]>opt[i - data[j]] + 1)) opt[i] = opt[i - data[j]] + 1; } printf("Case #%d\n%d\n", test_case, opt[Max]); } return 0; }
(三)另一种思路是考虑零钱种类
实现思路与过程来自01背包问题。
仅考虑一种零钱,得到每个面额的最优值。然后一个一个增加零钱种类,每增加一种零钱都刷新每个面额的最优值。
考虑当前面额由当前种零钱直接得到,是否能刷新当前面额的最优值。
与上一种思路相比,状态转移方程只是交换了for循环的顺序,最终结果均是对于每一个面额,考虑从所有种零钱直接得到。
#include <stdio.h> #define min(a,b) (a<b?a:b) int c, n, m[11], w, count[64001]; int main(){ scanf("%d", &c); for (int t = 1; t <= c; ++t){ scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", &m[i]); scanf("%d", &w); for (int i = 1; i <= w; ++i) count[i] = 0x3fffffff; for (int i = 1; i <= n; ++i)//背包种类 for (int j = m[i]; j <= w; ++j)//最大价值 count[j] = min(count[j], count[j - m[i]] + 1); printf("Case #%d\n%d\n", t, count[w]); } return 0; }
用于正确性测试的两组case:
2 3 1 4 6 8 6 1 4 5 7 16 20 4758
(四)补充
动态规划算法求解的问题具有最优子结构性质(一个问题的最优解包含其子问题的最优解)
和无后效性(将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态).

浙公网安备 33010602011771号