你给出一定数额的钱 i 元给我,我利用手中的硬币(m元, j元, k元...)兑换等值的钱给你,要求硬币数最少。
举例:给出1-11的钱,手中硬币有1元,3元,5元。
重点是找到状态和状态转移方程。
具体可以看这里:点击进入
引用自上面链接:
最终我们要求解的问题,可以用这个状态来表示:d(11),即凑够11元最少需要多少个硬币。 那状态转移方程是什么呢?既然我们用d(i)表示状态,那么状态转移方程自然包含d(i), 上文中包含状态d(i)的方程是:d(3)=min{d(3-1)+1, d(3-3)+1}。没错, 它就是状态转移方程,描述状态之间是如何转移的。当然,我们要对它抽象一下,
d(i)=min{ d(i-vj)+1 },其中i-vj >=0,vj表示第j个硬币的面值;
因此可以列出函数式:
const vector<int> Func(vector<int> &coin){ vector<int> MIN(12); for(int i = 1; i <= 11; ++i){//从1~11元,寻找每次最少兑换硬币数 MIN[i] = 99;//这里我理解为初始的状态值,赋值为不可能取值 for(int j = 0; j < coin.size(); ++j){ if(coin[j] <= i && MIN[i-coin[j]] + 1 < MIN[i]) MIN[i] = MIN[i - coin[j]] + 1; } } return MIN; }
coin[i] <= i表示,我给的硬币不能超过你给我的钱大小,不然亏了。
最长非降子序列的长度
先看看引言:
如果我们要求的这N个数的序列是:
5,3,4,8,6,7
根据上面找到的状态,我们可以得到:(下文的最长非降子序列都用LIS表示)
- 前1个数的LIS长度d(1)=1(序列:5)
- 前2个数的LIS长度d(2)=1(序列:3;3前面没有比3小的)
- 前3个数的LIS长度d(3)=2(序列:3,4;4前面有个比它小的3,所以d(3)=d(2)+1)
- 前4个数的LIS长度d(4)=3(序列:3,4,8;8前面比它小的有3个数,所以 d(4)=max{d(1),d(2),d(3)}+1=3)
OK,分析到这,我觉得状态转移方程已经很明显了,如果我们已经求出了d(1)到d(i-1), 那么d(i)可以用下面的状态转移方程得到:
d(i) = max{1, d(j)+1},其中j
用大白话解释就是,想要求d(i),就把i前面的各个子序列中, 最后一个数不大于A[i]的序列长度加1,然后取出最大的长度即为d(i)。 当然了,有可能i前面的各个子序列中最后一个数都大于A[i],那么d(i)=1, 即它自身成为一个长度为1的子序列。
代码:
#include#include using namespace std; int func(vector<int> &vec) { vector<int> MAX(vec.size()); int len = 1; for (int i = 0; i < vec.size(); ++i) { MAX[i] = 1; for (int j = 0; j < i; ++j) if (vec[j] <= vec[i] && MAX[j] + 1 > MAX[i]) MAX[i] = MAX[j] + 1; if (MAX[i] > len) len = MAX[i]; } return len; } int main() { vector<int> vec = { 5, 3, 4, 8, 6, 7}; cout << func(vec) << endl; system("PAUSE"); return 0; }
如果这道题我们改一下,要求最长连续非降序序列长度,又该是怎样呢?
#include#include using namespace std; int func(vector<int> &vec) { vector<int> MAX(vec.size()); int len = 1, i = 0, j = 0; for (; i < vec.size(); ++i) { MAX[i] = 1; for (; j < i; ++j) if (vec[j] <= vec[i] && MAX[j] + 1 > MAX[i]) MAX[i] = MAX[j] + 1; if (MAX[i] > len) len = MAX[i]; } return len; } int main() { vector<int> vec = { 5, 3, 4, 8, 6, 7}; cout << func(vec) << endl; system("PAUSE"); return 0; }