1. 程式人生 > >c++學習筆記:動態規劃(最長公共子序列,01揹包問題,金錢兌換問題)

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