dp.cpp

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

你可能感兴趣的:(dp.cpp)