#include <iostream> #include <string> #include <algorithm> #include <vector> using namespace std; // 动态规划求解背包问题 void knapsack(int weight[], int value[], int n, int W) { // V[i][j]表示i个物品放入承重为j的背包中 int **V = new int*[n + 1]; for (int i = 0; i <= n; i++) V[i] = new int[W + 1]; // V(0,j) = V(i,0) = 0 for (int j = 0; j <= W; j++) V[0][j] = 0; for (int i = 0; i <= n; i++) V[i][0] = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= W; j++) { if (j < weight[i - 1]) V[i][j] = V[i - 1][j]; else V[i][j] = max(V[i - 1][j], V[i - 1][j - weight[i - 1]] + value[i - 1]); } } cout << V[n][W] << endl; // 回溯显示 int i = n; int j = W; while (i > 0 && j > 0) { if (V[i][j] != V[i - 1][j]) { cout << "背包号:" << i << '(' << weight[i - 1] << ',' << value[i - 1] << ')' << endl;; j -= weight[i - 1]; i--; } else i--; } // 释放空间 for (int i = 0; i <= n; i++) delete[] V[i]; delete[] V; } // 动态规划求解背包问题,空间优化版本 void knapsack2(int weight[], int value[], int n, int W) { // a[i][j]表示i个物品放入承重为j的背包中 int **V = new int*[2]; for (int i = 0; i < 2; i++) V[i] = new int[W + 1]; // V(0,j) = V(i,0) = 0 for (int i = 0; i < 2; i++) for (int j = 0; j <= W; j++) V[i][j] = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= W; j++) { if (j < weight[i - 1]) V[1][j] = V[0][j]; else V[1][j] = max(V[0][j], V[0][j - weight[i - 1]] + value[i - 1]); } for (int j = 1; j <= W; j++) V[0][j] = V[1][j]; } cout << V[0][W] << endl; // 释放空间 for (int i = 0; i < 2; i++) delete[] V[i]; delete[] V; } // 最长公共子序列(非连续) void LCS(char a[], char b[]) { int lenA = strlen(a); int lenB = strlen(b); // L[i][j]表示长度为i的串和长度为j的串的最长公共子序列 int **L = new int*[lenA + 1]; for (int i = 0; i <= lenA; i++) L[i] = new int[lenB + 1]; // L[0][j] = L[i][0] = 0,表示当有一个字符串长度为0时,公共子串长度为0 for (int j = 0; j <= lenB; j++) L[0][j] = 0; for (int i = 0; i <= lenA; i++) L[i][0] = 0; for (int i = 1; i <= lenA; i++) { for (int j = 1; j <= lenB; 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]); } } cout << L[lenA][lenB] << endl; for (int i = 0; i <= lenA; i++) { for (int j = 0; j <= lenB; j++) cout << L[i][j] << ' '; cout << endl; } // 回溯显示 stack<char> trace; int i = lenA; int j = lenB; while (i > 0 && j > 0) { if (L[i][j] == L[i - 1][j - 1] + 1) { trace.push(a[i - 1]); j--; i--; } else if (L[i][j] == L[i - 1][j]) i--; else if (L[i][j] == L[i][j - 1]) j--; } while (!trace.empty()) { cout << trace.top(); trace.pop(); } // 释放空间 for (int i = 0; i <= lenA; i++) delete[] L[i]; delete[] L; } // 最长公共子串(连续) void lcs(char a[], char b[]) { int lenA = strlen(a); int lenB = strlen(b); // L[i][j]表示将a[i-1]和b[j-1]加入到公共子串末尾时,公共子串的长度 int **L = new int*[lenA + 1]; for (int i = 0; i <= lenA; i++) L[i] = new int[lenB + 1]; // L[0][j] = L[i][0] = 0,表示当有一个字符串长度为0时,公共子串长度为0 for (int j = 0; j <= lenB; j++) L[0][j] = 0; for (int i = 0; i <= lenA; i++) L[i][0] = 0; for (int i = 1; i <= lenA; i++) { for (int j = 1; j <= lenB; j++) { if (a[i - 1] == b[j - 1]) L[i][j] = L[i - 1][j - 1] + 1; else L[i][j] = 0; } } // 找出最长子串的长度 int max = 0; char start; char end; for (int i = 1; i <= lenA; i++) { for (int j = 1; j <= lenB; j++) { if (L[i][j] > max) { max = L[i][j]; start = i - max; end = i; } } } cout << "最大公共子串长度 = " << max << endl; while (start != end) cout << a[start++]; cout << endl; // 释放空间 for (int i = 0; i <= lenA; i++) delete[] L[i]; delete[] L; } // 找零问题:和为sum的组合中最少的硬币数目 void change(int coins[], int len, int sum) { int *d = new int[sum + 1]; // d[i]表示凑足i元最少需要的硬币个数 d[0] = 0; for (int i = 1; i <= sum; i++) { // d[i] = min(d[i-Vj] + 1) // 总数为i,拿了Vj,余额的最少硬币个数为d[i-Vj] d[i] = d[i - coins[0]] + 1; for (int j = 1; j < len; j++) { if (i >= coins[j] && d[i - coins[j]] + 1 < d[i]) d[i] = d[i - coins[j]] + 1; } } cout << d[sum] << endl; delete[] d; } // 找零问题:n种硬币组成和为sum的组合数 // 每种硬币个数不限,这是一个完全背包问题 void change2(int coins[], int n, int sum) { // d[i][j]表示用前i个硬币组成和为j的组合数 int **d = new int*[n + 1]; for (int i = 0; i <= n; i++) d[i] = new int[sum + 1]; // 记得先清零 for (int i = 0; i <= n; i++) for (int j = 0; j <= sum; j++) d[i][j] = 0; // d[0][j] = 0,表示没有硬币时,不能形成任何组合 for (int j = 0; j <= sum; j++) d[0][j] = 0; // d[i][0] = 1,表示和为0的组合方式有一种:所有硬币都不选 for (int i = 0; i <= n; i++) d[i][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= sum; j++) { // 第i个硬币最多可以拿n个 int n = j / coins[i - 1]; for (int k = 0; k <= n; k++) { d[i][j] += d[i - 1][j - k*coins[i - 1]]; } } } cout << d[3][10] << endl; // 释放空间 for (int i = 0; i <= n; i++) delete[] d[i]; delete[] d; } // 青蛙跳台阶,一次可以跳1-n阶,有多少种跳法 void stage(int n) { int *s = new int[n + 1]; s[0] = 1; for (int i = 1; i <= n; i++) s[i] = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j < i; j++) s[i] += s[j]; } cout << s[n] << endl; delete[] s; } // 最长递增子序列(非连续) void LIS(int a[], int len) { // L[i]表示包含a[i]在内,最长递增子序列的长度 int *L = new int[len]; // 显然第一个元素只包含它自己,所以L[0] = 1 L[0] = 1; for (int i = 1; i < len; i++) { L[i] = 1; for (int j = 0; j < i; j++) { if (a[j] < a[i] && L[j] + 1 > L[i]) L[i] = L[j] + 1; } } // 显示 int cnt = 1; for (int i = 0; i < len; i++) { if (L[i] == cnt) { cout << a[i] << ' '; cnt++; } } cout << endl; delete[] L; } // 求卡特兰数的第n项,n为某种物品的对数 int Catalan(int n) { int *c = new int[n + 1]; c[0] = 1; for (int i = 1; i <= n; i++) { c[i] = 0; for (int j = 0; j < i; j++) c[i] += c[j] * c[i - j - 1]; } for (int i = 0; i <= n; i++) cout << c[i] << endl; int res = c[n]; delete[] c; return res; } // 子数组之和的最大值(动态规划解法) int LongestSubArray(int a[], int len) { // L[i]表示以a[i]结尾的子数组最大和 int *L = new int[len]; L[0] = a[0]; for (int i = 1; i < len; i++) { // 前面部分和为负数,丢弃 if (L[i - 1] <= 0) L[i] = a[i]; else L[i] = L[i - 1] + a[i]; } int max = L[0]; for (int i = 0; i < len; i++) if (L[i] > max) max = L[i]; return max; } // 最大连续乘积子串 double MaxProductSequence(double a[], int len) { // Max[i]表示以a[i]结尾的最大连续子串的乘积值 double *Max = new double[len]; // Min[i]表示以a[i]结尾的最小连续子串的乘积值 double *Min = new double[len]; Max[0] = a[0]; Min[0] = a[0]; double res = Max[0]; for (int i = 1; i < len; i++) { Max[i] = max(max(a[i], Max[i - 1] * a[i]), Min[i - 1] * a[i]); Min[i] = min(min(a[i], Max[i - 1] * a[i]), Min[i - 1] * a[i]); if (Max[i] > res) res = Max[i]; } /*for (int i = 0; i < len; i++) cout << Max[i] << ' '; cout << endl;*/ delete[] Max; delete[] Min; return res; } // 递归法计算字符串距离(Levenshtein距离) int StringDistance(char *str1, char *str2) { // str1、str2不能为NULL if (*str1 == '\0') { if (*str2 == '\0') return 0; else return strlen(str2); } if (*str2 == '\0') { if (*str1 == '\0') return 0; else return strlen(str1); } if (*str1 == *str2) return StringDistance(str1 + 1, str2 + 1); else { int d1 = StringDistance(str1 + 1, str2); // str2增加一个字符 int d2 = StringDistance(str1, str2 + 1); // str2删除一个字符 int d3 = StringDistance(str1 + 1, str2 + 1); // str2修改一个字符 return min(min(d1, d2), d3) + 1; } } // 动态规划计算字符串距离(Levenshtein距离) int StringDistance_DP(char *str1, char *str2) { int len1 = strlen(str1); int len2 = strlen(str2); // D[i][j]表示str1[0]~str1[i-1]和str2[0]~str2[j-1]之间的字符串距离 int **D = new int*[len1 + 1]; for (int i = 0; i <= len1; i++) D[i] = new int[len2 + 1]; // str2长度为0,距离为str1的长度 for (int i = 0; i <= len1; i++) D[i][0] = i; // str1长度为0,距离为str2的长度 for (int j = 0; j <= len2; j++) D[0][j] = j; for (int i = 1; i <= len1; i++) { for (int j = 1; j <= len2; j++) { // 若str1[i-1]和str2[j-1]相等,则距离不增加 if (str1[i - 1] == str2[j - 1]) D[i][j] = D[i - 1][j - 1]; else // D[i][j - 1] + 1:删除str2[j-1],长度+1 // D[i - 1][j] + 1:删除str1[i-1],长度+1 // 若str1[i-1]和str2[j-1]不相等,则执行替换操作,距离+1 D[i][j] = min(min(D[i][j - 1] + 1, D[i - 1][j] + 1), D[i - 1][j - 1] + 1); } } for (int i = 0; i <= len1; i++) { for (int j = 0; j <= len2; j++) cout << D[i][j] << ' '; cout << endl; } int res = D[len1][len2]; for (int i = 0; i <= len1; i++) delete[] D[i]; delete[] D; return res; }