c++學習筆記:動態規劃(最長公共子序列,01揹包問題,金錢兌換問題)
阿新 • • 發佈:2019-02-18
/*
參考書:演算法設計技巧與分析 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;
}