c++学习笔记:动态规划(最长公共子序列,01背包问题,金钱兑换问题)

/*
参考书:算法设计技巧与分析  M.H.Alsuwaiyel著 吴伟旭 方世昌译
----------------------------------------------------------------
1.递归 将问题分成相似的子问题
1.1Fabonacci问题
递归会导致重复计算,时间复杂度为o(2^n),因为函数压栈,空间复杂度o(n)
非递归 时间复杂度为o(n),空间复杂度为o(1)
----------------------------------------------------------------
2. 动态规划
 找到递推公式,从底向上,再计算下一个问题时,上一个问题已经解决,可以直接调用之前的结果
 一般都是对于问题建一个矩阵,找到递推公式

2.1 最长公共子序列 LCS
对于 a1a2.....ai  and  b1b2......bj 最长公共子序列的长度为 L[i,j]
if i==0||j==0 then L[i,j]=0
else if ai==bj then L[i,j]=l[i-1,j-1]+1 else L[i,j]=max{L[i-1,j],L[i,j-1]}
2.2 矩阵链相乘

2.3 背包问题 KNAPSACK
0/1背包问题
将n个物品放入C大小的背包中,求V[n,C]的最大值,放入物品的最大价值
V[i,j]表示将前i个物品放入大小为j的空间中的最大价值,V的size是n+1 * C+1
si,vi分别是物品ui的大小和价值
if i==0||j==0 then V[i,j]=0;
else if j<si then V[i,j]=V[i-1,j]
else if i>0&&j>=si then v[i,j]=max{V[i-1,j],V[i-1,j-si]+vi}

2.4 金钱兑换问题 money_exchange
一个货币系统,有n种硬币,面值为 v1v2v3....vn,其中v1=1,
现在兑换价值为y的钱,要让硬币数目最少
即在约束 sum(xi*vi)=y的情况下 sum(xi)最小 其中i=[1....n]

N[i,j]表示前i种硬币兑换j的钱最少的硬币数 
初始化 N[0][j]=INF N[1][j]=j N[i][0]=0 
if vi>j then N[i,j]=N[i-1,j] else N[i,j]=min{N[i-1,j-kvi]+k,N[i-1,j]}
return N[n][y]
----------------------------------------------------------------
2.最短路径问题(Dijkstra)

-----------------------------------------------------------------
3.最小耗费生成树(Kruskal)

4.最小耗费生成树(Prim)

*/


#include <iostream>
#include <vector>
#include <array>
#include <list>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <random>
#include <Windows.h>
using namespace std;
std::default_random_engine generator;
#define INF 99999
/*
均匀分布uniform,正态分布normal,二项分布binomial,泊松分布poisson
*/
std::uniform_int_distribution<int> dis(1, 9);

void myRandom(vector<int>&datasets, int n) {
    for (int i = 0; i < n;i++)
        datasets.push_back(dis(generator));
}

int Fibonacci1(int n) {
    if (n == 1 || n == 2) return 1;
    return Fibonacci1(n - 1) + Fibonacci1(n - 2);
}
int Fibonacci2(int n) {
    //assert(n >= 0);
    int first = 0;
    int secend = 1;
    int third = 0;
    for (int i = 2; i <= n; i++) {
        third = secend + first;
        first = secend;
        secend = third;
    }
    return third;
}
//最长公共子序列
int LCS(int n, int m) {
    vector<int> a, b;//序列a,b
    myRandom(a, n);
    myRandom(b, m);
    for_each(a.begin(), a.end(), [](auto &it) {cout << it << '\t'; });
    cout << endl;
    for_each(b.begin(), b.end(), [](auto &it) {cout << it << '\t'; });
    cout << endl;
    //矩阵大小是n+1 * m+1
    vector<vector<int>> L(n+1);
    for_each(L.begin(), L.end(), [&](auto &each) {swap(each, vector<int>(m+1, 0)); });
    //for (int i = 0; i < n; i++)
    //  L.push_back(vector<int>(m, 0));
    //for (auto it = L.begin(); it != L.end(); it++)
    //  cout << it->at(1) << endl;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {//注意边界
            if (a[i - 1] == b[j - 1]) L[i][j] = L[i - 1][j - 1] + 1;
            else L[i][j] = max(L[i - 1][j], L[i][j - 1]);
        }
    }
    return L[n][m];
}

//0/1背包问题
int KNAPSACK(int n,int C) {
    vector<int> s;//大小
    vector<int> v;//价值
    myRandom(s, n);
    myRandom(v, n);
    for_each(s.begin(), s.end(), [](auto &it) {cout << it << '\t'; });
    cout << endl;
    for_each(v.begin(), v.end(), [](auto &it) {cout << it << '\t'; });
    cout << endl;
    vector<vector<int>>V(n+1);
    for_each(V.begin(), V.end(), [&](auto &each) {swap(each, vector<int>(C + 1, 0)); });
    //for (auto it = V.begin(); it != V.end(); it++)
    //  cout << it->at(1) << endl;
    for (int i = 1; i <= n; i++) { //注意下标的意义,i对应的物体是s[i-1],和V有点不同
        for (int j = 1; j <= C; j++) {
            if (j < s[i-1]) V[i][j] = V[i - 1][j];
            else if (j >= s[i-1]) V[i][j] = max(V[i - 1][j] ,V[i - 1][j - s[i-1]] + v[i-1]);
        }
    }
    return V[n][C];
}
//金钱兑换问题 P143 (7.30)
int MoneyExchange(int n, int y) {
    if (n == 1) return y;
    vector<int> v;//硬币面值
    myRandom(v, n);
    v[0] = 1;
    sort(v.begin(), v.end());//其实这一步排序不必要,因为保证了了v[0]=1是基本硬币
    for_each(v.begin(), v.end(), [](auto &it) {cout << it << '\t'; });
    cout << endl;
    vector<vector<int>> N(n+1);
    for_each(N.begin(), N.end(), [&](auto &each) {swap(each, vector<int>(y + 1, INF)); });
    for (int i = 0; i <= y; i++) {
        N[1][i] = i;
    }
    for (int i = 1; i <= n; i ++ ) {
        N[i][0] = 0;
    }
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= y; j++) {
            N[i][j] = N[i - 1][j];//如果不要这个v[i-1]
            if (v[i - 1] <= j) {
                int num = j / v[i - 1];
                vector<int> temp;
                //temp.push_back(N[i][j]);
                for (int k = 0; k <= num; k++) {
                    temp.push_back(N[i - 1][j - k*v[i - 1]] + k);
                }
                sort(temp.begin(), temp.end());
                N[i][j] = temp[0];
            }
        }
    }
    return N[n][y];
}

void test_Fibonacci(int n) {
    cout << "Fibonacci:\n";
    long t1 = GetTickCount();
    cout << Fibonacci2(n) << endl;
    long t2 = GetTickCount();
    cout << t2 - t1 << endl;
    cout << Fibonacci1(n) << endl;
    long t3= GetTickCount();
    cout << t3 - t2 << endl;
    //cout << vec.size() << endl;
}
void test() {
    //test_Fibonacci(35);
    cout << "LCS:\n";
    cout<<LCS(10, 10)<<endl;

    cout << "KNAPSACK:\n";
    cout << KNAPSACK(5, 20)<<endl;

    cout << "MoneyExchange:\n";
    cout << MoneyExchange(6, 100) << endl;
}
int main() {
    test();
    system("pause");
    return 0;
}
发布了19 篇原创文章 · 获赞 6 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u010548772/article/details/80808656