递归与动态规划之五—1

汉诺塔问题

题目

给定一个整数n,代表汉诺塔游戏中从小到大放置的n个圆盘,假设开始时所有的圆盘都放在左边的柱子上,想按照汉诺塔游戏的要求把所有的圆盘都放到右边的柱子上,实现函数打印最优移动轨迹。
进阶题目可参考原书

代码

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;

void func(int n, string from, string mid, string to)
{
	if (n == 1)
		cout << "move from" + from + " to " + to << endl;
	else
	{
		func(n - 1, from, to, mid);
		func(1, from, mid, to);
		func(n - 1, mid, from, to);
	}
}

void hanoi(int n)
{
	if (n > 0)
		func(n, "left", "mid", "right");
}

//判断一个数组是否为最优轨迹中的一个状态,若是,判断为第几个状态
int process(vector<int> arr, int i, int from, int mid, int to)
{
	if (i == -1)
		return 0;
	if (arr[i] != from && arr[i] != to)
		return -1;
	if (arr[i] == from)
		return process(arr, i - 1, from, to, mid);
	else
	{
		int rest = process(arr, i - 1, mid, from, to); 
		//转移的顺序要分清楚,如果错误则最后结果出错
		if (rest == -1)
			return -1;
		return (1 << i) + rest;
	}
}

int step1(vector<int> arr)
{
	if (arr.size() < 1)
		return -1;
	return process(arr, arr.size() - 1, 1, 2, 3);
}//时间复杂度可以达到O(N),但递归对栈的使用使空间复杂度达到o(N)

//非递归的方法
int step2(vector<int> arr)
{
	if (arr.size() < 1)
		return -1;
	int from = 1;
	int mid = 2;
	int to = 3;
	int i = arr.size() - 1;
	int res = 0;
	int tmp = 0;
	while (i >= 0)
	{
		if (arr[i] != from && arr[i] != to)
			return -1;
		if (arr[i] == to)
		{
			res += 1 << i;
			tmp = from;
			from = mid;
		}
		else
		{
			tmp = to;
			to = mid;
		}
		mid = tmp;
		i--;
	}
	return res;
}

int main()
{
	int n; 
	cin >> n;
	vector<int>in(n, 0);
	for (int i = 0; i < n; i++)
		cin >> in[i];
	hanoi(n);
	int res1 = step1(in);
	int res2 = step2(in);
	cout << res1 << res2 << endl;
	getchar();
	return 0;
}

最长公共子序列问题

题目

给定两个字符串str1和str2,返回两个字符串的最长公共子序列。

代码

详细分析过程参考原书

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;

vector<vector<int>> getdp(string str1, string str2)
{
	int row = str1.size();
	int col = str2.size();
	vector<vector<int>> dp(row, vector<int>(col, 0));
	dp[0][0] = str1[0] == str2[0] ? 1 : 0;
	for (int i = 1; i < row; i++)
	{
		dp[i][0] = max(dp[i - 1][0], str1[i] == str2[0] ? 1 : 0);
	}
	for (int j = 1; j < col; j++)
	{
		dp[0][j] = max(dp[0][j - 1], str1[0] == str2[j] ? 1 : 0);
	}
	for (int i = 1; i < row; i++)
	{
		for (int j = 1; j < col; j++)
		{
			dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
			if (str1[i] == str2[j])
				dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
		}
	}
	return dp;
}

string maxLCS(string str1, string str2)
{
	if (str1.size() < 1 || str1.size() < 1 || str1 == "" || str2 == "")
		return "";
	vector<vector<int>> dp = getdp(str1, str2);
	int m = str1.size() - 1;
	int n = str2.size() - 1;
	int index = dp[m][n];
	string res(index, 'a');
	index--;
	while (index >= 0)
	{
		if (n > 0 && dp[m][n] == dp[m][n - 1])
			n--;
		else if (m > 0 && dp[m][n] == dp[m - 1][n])
			m--;
		else
		{
			res[index--] = str1[m];
			m--;
			n--;
		}
	}
	return res;
}

int main()
{
	string s1, s2;
	getline(cin, s1);
	getline(cin, s2);
	string res = maxLCS(s1, s2);
	cout << res << endl;
	getchar();
	return 0;
}

/*dp矩阵的获取比较简单,dp[i][j]的取值只有三种,分别为dp[i-1][j],
dp[i][j-1],dp[i-1][j-1]+1;
重建最长公共子序列的过程的关键点是dp[i][j] = dp[i-1][j-1]+1,剩余
的两种情况都可以略过不去考虑*/

最长公共字串问题

题目

给定两个字符串str1和str2,返回两个字符串的最长公共子串。
要求:如果str1的长度为M,str2长度为N,实现时间复杂度为O(M×N),额外空间复杂度为O(1)的方法。

代码

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;

vector<vector<int>> getdp(string s1, string s2)
{
	int row = s1.size();
	int col = s2.size();
	vector<vector<int>> dp(row, vector<int>(col, 0));
	for (int i = 0; i < row; i++)
	{
		if (s1[i] == s2[0])
			dp[i][0] = 1;
	}
	for (int j = 0; j < col; j++)
	{
		if (s1[0] == s2[j])
			dp[0][j] = 1;
	}
	for (int i = 1; i < row; i++)
	{
		for (int j = 1; j < col; j++)
		{
			if (s1[i] == s2[j])
				dp[i][j] = dp[i - 1][j - 1] + 1;
		}
	}
	return dp;
}

string lcst1(string s1, string s2)
{
	if (s1.size() < 1 || s2.size() < 1 || s1 == "" || s2 == "")
		return "";
	vector<vector<int>> dp = getdp(s1, s2);
	int row = s1.size();
	int col = s2.size();
	int end = 0;
	int maxV = 0;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (dp[i][j] > maxV)
			{
				end = i;
				maxV = dp[i][j];
			}
		}
	}
	return s1.substr(end - maxV + 1, maxV);
}

string lcst2(string s1, string s2)
{
	if (s1.size() < 1 || s2.size() < 1 || s1 == "" || s2 == "")
		return "";
	int row = s1.size();
	int col = s2.size();
	int m = 0;
	int n = col - 1;
	int maxV = 0;
	int end = 0;
	while (m < row)
	{
		int i = m;
		int j = n;
		int len = 0;
		while (i < row && j < col)
		{
			if (s1[i] != s2[j])
				len = 0;
			else
				len++;
			if (len > maxV)
			{
				end = i;
				maxV = len;
			}
			i++;
			j++;
		}
		if (n > 0)
			n--;
		else
			m++;
	}
	return s1.substr(end - maxV + 1, maxV);
}

int main()
{
	string s1, s2;
	getline(cin, s1);
	getline(cin, s2);
	string res1 = lcst1(s1, s2);
	string res2 = lcst2(s1, s2);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

/*思路与最长公共子序列问题基本上一致,只是在dp矩阵更新是可以将算法
的空间复杂度压缩至O(1),只记录最终位置和最大公共字串的长度即可*/

最小编辑代价

题目

给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,返回将str1编辑成str2的最小代价。

代码

#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;

int minCost1(string s1, string s2, int ic, int dc, int rc)
{
	if (s1.size() < 1 || s2.size() < 1)
		return 0;
	int row = s1.size() + 1;
	int col = s2.size() + 1;
	vector<vector<int>> dp(row, vector<int>(col, 0));
	for (int i = 1; i < row; i++)
	{
		dp[i][0] = dc * i;
	}
	for (int j = 1; j < col; j++)
		dp[0][j] = ic * j;
	//找到dp的更新方法
	for (int i = 1; i < row; i++)
	{
		for (int j = 1; j < col; j++)
		{
			if (s1[i - 1] == s2[j - 1])
				dp[i][j] = dp[i - 1][j - 1];
			else
				dp[i][j] = dp[i - 1][j - 1] + rc;
			dp[i][j] = min(dp[i][j], dp[i][j - 1] + ic);
			dp[i][j] = min(dp[i][j], dp[i - 1][j] + dc);
		}
	}
	return dp[row - 1][col - 1];
}

//空间压缩
int minCost2(string s1, string s2, int ic, int dc, int rc)
{
	if (s1.size() < 1 || s2.size() < 1)
		return 0;
	int more = s1.size() > s2.size() ? s1.size() : s2.size();
	int less = s1.size() > s2.size() ? s2.size() : s1.size();
	string longs = s1.size() > s2.size() ? s1 : s2;
	string shorts = s1.size() > s2.size() ? s2 : s1;
	if (s1.size() < s2.size())
	{
		int tmp = ic;
		ic = dc;
		dc = tmp;
		//如果s2较长,那么交换ic和dc的值??
	}
	vector<int>dp(less + 1, 0);
	for (int i = 1; i <= less; i++)
		dp[i] = ic * i;
	for (int i = 1; i <= more; i++)
	{
		int pre = dp[0]; //pre表示左上角的值
		dp[0] = dc * i;
		for (int j = 1; j <= less; j++)
		{
			int tmp = dp[j];  //dp[j]未更新前先保存下来
			if (longs[i - 1] == shorts[j - 1])
				dp[j] = pre;
			else
				dp[j] = pre + rc;
			dp[j] = min(dp[j], dp[j - 1] + ic);
			dp[j] = min(dp[j], tmp + dc);
			pre = tmp;
		}
	}
	return dp[less];
}

int main()
{
	string s1, s2;
	getline(cin, s1);
	getline(cin, s2);
	int ic, dc, rc;
	cin >> ic >> dc >> rc;
	int res1 = minCost1(s1, s2, ic, dc, rc);
	int res2 = minCost2(s1, s2, ic, dc, rc);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

字符串的交错组成

题目

给定三个字符串str1,str2和aim,如果aim包含且仅包含来自str1和str2的所有字符,而且在aim中属于str1的字符之间保持原来在str1中的顺序,属于str2的字符之间保持原来在str2中的顺序,那么称aim是str1和str2的交错组成。实现一个函数,判断aim是否是str1和str2的交错组成。

代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;

bool isCross1(string s1, string s2, string aim)
{
	if (s1.size() < 1 || s2.size() < 1 || aim.size() < 1)
		return false;
	if (s1.size() + s2.size() != aim.size())
		return false;
	int row = s1.size();
	int col = s2.size();
	vector<vector<bool>> dp(row + 1, vector<bool>(col + 1, false));
	dp[0][0] = true;
	for (int i = 1; i <= row; i++)
	{
		if (s1[i - 1] != aim[i - 1])
			break;
		dp[i][0] = true;
	}
	for (int j = 1; j <= col; j++)
	{
		if (s2[j - 1] != aim[j - 1])
			break;
		dp[0][j] = true;
	}
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if ((s1[i - 1] == aim[i + j - 1] && dp[i - 1][j])
				|| (s2[j - 1] == aim[i + j - 1] && dp[i][j - 1]))
			{
				dp[i][j] = true;
			}
		}
	}
	return dp[row][col];
}

/*空间压缩至O(min(m, n))*/
bool isCross2(string s1, string s2, string aim)
{
	if (s1.size() < 1 || s2.size() < 1 || aim.size() < 1)
		return false;
	if (s1.size() + s2.size() != aim.size())
		return false;
	string longs = s1.size() > s2.size() ? s1 : s2;
	string shorts = s1.size() > s2.size() ? s2 : s1;
	int more = longs.size();
	int less = shorts.size();
	vector<bool> dp(less + 1, false);
	dp[0] = true;
	for (int i = 1; i <= less; i++)
	{
		if (shorts[i - 1] != aim[i - 1])
			break;
		dp[i] = true;
	}
	for (int i = 1; i <= more; i++)
	{
		dp[0] = dp[0] && longs[i - 1] == aim[i - 1];
		for (int j = 1; j <= less; j++)
		{
			if ((longs[i - 1] == aim[i + j - 1] && dp[j])
				|| (shorts[j - 1] == aim[i + j - 1] && dp[j - 1]))
			{
				dp[j] = true;
			}
			else
				dp[j] = false;
		}
	}
	return dp[less];
}

int main()
{
	string s1, s2, aim;
	getline(cin, s1);
	getline(cin, s2);
	getline(cin, aim);
	bool res1 = isCross1(s1, s2, aim);
	bool res2 = isCross2(s1, s2, aim);
	if (res1 && res2)
		cout << "Two functions are right!" << endl;
	else
		cout << "Not same!" << endl;
	getchar();
	return 0;
	
}
/*关键点:
1、dp[i][j]代表aim[0..i+j-2]能否被str1[0..i-2]和str2[0..j-1]交错组成,如果可以,那么如果再有str1[i-1]等于aim[i+j-1],说明str1[i-1]又可以作为交错组成aim[0..i+j-1]的最后一个字符。令dp[i][j]=true
2、dp[i][j-1]代表aim[0..i+j-2]能否被str1[0..i-1]和str2[0..j-2]交错组成,如果可以,那么如果再有str2[j-1]等于aim[i+j-1],说明str1[j-1]可以做恶日交错组成aim[0..i+j-1]的最后一个字符。令dp[i][j]=true。
3、如果不满则第一种和第二种情况,令dp[i][j]=false*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值