DP:凑零钱问题/最长非降子序列(C++)

你给出一定数额的钱 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<i,A[j]<=A[i]

用大白话解释就是,想要求d(i),就把i前面的各个子序列中, 最后一个数不大于A[i]的序列长度加1,然后取出最大的长度即为d(i)。 当然了,有可能i前面的各个子序列中最后一个数都大于A[i],那么d(i)=1, 即它自身成为一个长度为1的子序列。

 

代码:

#include <iostream>
#include <vector>

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 <iostream>
#include <vector>

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;
}

 

posted @ 2019-03-20 21:14  Hk_Mayfly  阅读(636)  评论(0编辑  收藏  举报