动态规划练习题

来源:互联网 发布:现在淘宝开店步骤 编辑:程序博客网 时间:2024/06/06 16:10

最近学习算法,刚刚接触动态规划,整了一下午终于整对一道题,发到博客供交流及复习。

先大致总结一下自己理解的动态规划。

动态规划的两个特点:

1,当前状态的最优解可以由上一个状态的最优解推出(子问题有最优解)

2,整个求解过程中许多状态会被多次求解(重叠子问题)

解决动态规划问题时,首先要自顶向下分析(求得递归式),再自底向上写代码(避免重复求解子问题)。

题目如下:

假设我们有8种不同面值的硬币{1,2,5,10,20,50,100,200},用这些硬币组合够成一个给定的数值n。例如n=200,那么一种可能的组合方式为 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 问总过有多少种可能的组合方式?(原题及数学分析见http://www.cnblogs.com/python27/p/3303721.html

自顶向下的分析见上述链接,我这里给出自底向上的分析。

设一个200*8的二维数组c[200][8],令c[i][j]的值表示:用前j个硬币组合出数值i的所有可能组合总数,如c[100][3]表示用面值为1、2和5的硬币组合成100的可能组合数,则可以得出如下矩阵:


上述矩阵每个元素c[i][j] = sum(c[k][j-1])

其中k=(i, i-1*Vj, i-2*Vj, ……,i mod Vj)。Vj表示子问题由由前j-1个硬币转移到前j个硬币时所添加的硬币面值(如由前2个硬币可用转移到前3个硬币可用时,添加的硬币面值为5,则V3=5)

例如c[6][3] = c[6][2]+c[1][2],其实从这个例子很容易理解其中的原理:c[6][3](用1、2、5三个硬币组合成6的组合数)等于两个子问题的和,一个子问题是c[6][2](用1、2组合成6的组合数,即使用任意数量的1、2而使用0个5),另一个子问题是c[1][2](用1、2组合成1的组合数,即使用任意数量的1、2同时使用1个5

附代码:

#include <iostream>using namespace std;/*void print(int **a, int xx, int yy) {for (int x = 0; x < xx; x++) {for (int y = 0; y < yy; y++) {cout << a[x][y] << " ";}cout << endl;}}*/int GetCombines(int CoinValue[], int CoinNumber, int Goal) {int **dp = new int*[Goal + 1];int ret;for (int i = 0; i <= Goal; i++) {dp[i] = new int[CoinNumber];}for (int i = 0; i <= CoinNumber; i++) {dp[0][i] = 1;}for (int j = 1; j <= CoinNumber; j++) {for (int i = 1; i <= Goal; i++) {for (int k = i; k >= 0; k = k - CoinValue[j - 1]) {dp[i][j] += dp[k][j - 1];}}}//print(dp, Goal, CoinNumber);ret = dp[Goal][CoinNumber];for (int i = 0; i <= Goal; i++) {delete dp[i];}delete dp;return ret;}int main() {int CoinValue[] = { 1, 2, 5, 10, 20, 50, 100, 200 };int CoinNumber = 8;int Goal = 200;cout << GetCombines(CoinValue, CoinNumber, Goal) << endl;}


0 0
原创粉丝点击