递归与动态规划之六

龙与地下城游戏

题目

给定一个二维数组map,含义是一张地图。游戏规则如下:
1、骑士从左上角出发,每次只能向右或向下走,最后到达右下角见到公主。
2、地图中每个位置的值表示骑士的遭遇。如果是负数,此处有怪兽,要让骑士损失血量。如果是非负数,代表此处有血瓶,可以让骑士回血。
3、骑士从左上角到右下角的过程中,走到任何一个位置时,血量都不能少于1.
为了保证能见到公主,初始血量至少是多少?根据map,返回初始血量。

代码

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

int minHP1(vector<vector<int>> mp)
{
	if (mp.size() < 1 || mp[0].size() < 1)
		return 1;
	int row = mp.size();
	int col = mp[0].size();
	vector<vector<int>> dp(row, vector<int>(col, 0));
	dp[row - 1][col - 1] = mp[row - 1][col - 1] > 0 ? 1 : -mp[row - 1][col - 1] + 1;
	for (int j = col - 2; j >= 0; j--)
	{
		dp[row - 1][j] = max(dp[row - 1][j + 1] - mp[row - 1][j], 1);
	}
	int right = 0;
	int down = 0;
	for (int i = row - 2; i >= 0; i--)
	{
		dp[i][col-1] = max(dp[i + 1][col - 1] - mp[i][col - 1], 1);
		for (int j = col - 2; j >= 0; j--)
		{
			right = max(dp[i][j + 1] - mp[i][j], 1);
			down = max(dp[i + 1][j] - mp[i][j], 1);
			dp[i][j] = min(right, down);
		}
	}
	return dp[0][0];
}

//空间压缩
int minHp2(vector<vector<int>> mp)
{
	if (mp.size() < 1 || mp[0].size() < 1)
		return 1;
	int more = max(mp.size(), mp[0].size());
	int less = min(mp.size(), mp[0].size());
	bool rowMore = more == mp.size();
	vector<int> dp(less, 0);
	int tmp = mp[mp.size() - 1][mp[0].size() - 1];
	dp[less - 1] = tmp > 0 ? 1 : -tmp + 1;
	int row = 0;
	int col = 0;
	for (int j = less - 2; j >= 0; j--)
	{
		row = rowMore ? more - 1 : j;
		col = rowMore ? j : more - 1;
		dp[j] = max(dp[j + 1] - mp[row][col], 1);
	}
	int choosen1 = 0;
	int choosen2 = 0;
	for (int i = more - 2; i >= 0; i--)
	{
		row = rowMore ? i : less - 1;
		col = rowMore ? less - 1 : i;
		dp[less - 1] = max(dp[less - 1] - mp[row][col], 1);
		for (int j = less - 2; j >= 0; j--)
		{
			row = rowMore ? i : j;
			col = rowMore ? j : i;
			choosen1 = max(dp[j] - mp[row][col], 1);
			choosen2 = max(dp[j + 1] - mp[row][col], 1);
			dp[j] = min(choosen1, choosen2);
		}
	}
	return dp[0];
}

int main()
{
	int m, n;
	cin >> m >> n;
	vector<vector<int>> in(m, vector<int>(n, 0));
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			cin >> in[i][j];
		}
	}
	int res1 = minHP1(in);
	int res2 = minHp2(in);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

数字字符串转换为字母组合的种数

题目

给定一个字符串str,str全部由数字字符组成,如果str中某一个或某相邻两个字符组成的子串值在1~26之间,则这个子串可以转换成一个字母。规定‘1’转换为‘A’,‘2’转换为‘B’,…,‘26’转换成‘Z’。实现一个函数,求str有多少种不同的转换结果,并返回种数。

代码

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

//递归
int process(string str, int i)
{
	if (i == str.size())
		return 1;
	if (str[i] == '0')
		return 0;
	int res = process(str, i + 1);
	if (i + 1 < str.size() && (str[i] - '0') * 10 + str[i + 1] - '0' < 27)
		res += process(str, i + 2);
	return res;
}
int num1(string str)
{
	if (str.size() < 1 || str == "")
		return 0;
	return process(str, 0);
}

int num2(string str)
{
	if (str.size() < 1|| str == "")
		return 0;
	int cur = str[str.size() - 1] == '0' ? 0 : 1;
	int next = 1;
	int tmp = 0;
	for (int i = str.size() - 2; i >= 0; i--)
	{
		if (str[i] == '0')
		{
			next = cur;
			cur = 0;
		}
		else
		{
			tmp = cur;
			if ((str[i] - '0') * 10 + str[i + 1] - '0' < 27)
				cur += next;
			next = tmp;
		}
	}
	return cur;
}

int main()
{
	string s;
	getline(cin, s);
	int res1 = num1(s);
	int res2 = num2(s);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

表达式得到期望结果的组成种数

题目

给定一个只由0(假)、1(真)、&(逻辑与)、|(逻辑或)和^(异或)五种字符组成的字符串express。再给定一个布尔值desired。返回express能有多少种组合方式,可以达到desired的成果。

代码

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

/*首先判断表达式是否有效,以下几个标准:1、表达式长度必须为奇数
2、表达式下标为偶数的位置的字符一定是‘0’或‘1’
3、表达式下标为奇数位置的字符一定是‘&’或‘|’或'^'*/
bool isValid(string str)
{
	if ((str.size() & 1) == 0)
		return false;
	int len = str.size();
	for (int i = 0; i < len; i = i + 2)
	{
		if ((str[i] != '1') && (str[i] != '0'))
			return false;
	}
	for (int i = 1; i < len; i += 2)
	{
		if ((str[i] != '&') && (str[i] != '|') && (str[i] != '^'))
			return false;
	}
	return true;
}

/*如果为真之后,递归判断有多少种划分方式,累加给定结果的不同划分方式
得到最后结果*/

int p(string str, bool desired, int l, int r)
{
	if (l == r)
	{
		if (str[l] == '1')
			return desired ? 1 : 0;
		else
			return desired ? 0 : 1;
	}
	int res = 0;
	if (desired)
	{
		for (int i = l + 1; i < r; i += 2)
		{
			switch (str[i])
			{
			case '&':
				res += p(str, true, l, i - 1) * p(str, true, i + 1, r);
				break;
			case '|':
				res += p(str, true, l, i - 1) * p(str, false, i + 1, r);
				res += p(str, false, l, i - 1) * p(str, true, i + 1, r);
				res += p(str, true, l, i - 1) * p(str, true, i + 1, r);
				break;
			case '^':
				res += p(str, true, l, i - 1) * p(str, false, i + 1, r);
				res += p(str, false, l, i - 1) * p(str, true, i + 1, r);
				break;
			}
		}
	}
	else
	{
		for (int i = l + 1; i < r; i += 2)
		{
			switch (str[i])
			{
			case '&':
				res += p(str, false, l, i - 1) * p(str, true, i + 1, r);
				res += p(str, true, l, i - 1) * p(str, false, i + 1, r);
				res += p(str, false, l, i - 1) * p(str, false, i + 1, r);
				break;
			case '|':
				res += p(str, false, l, i - 1) * p(str, false, i + 1, r);
				break;
			case '^':
				res += p(str, true, l, i - 1) * p(str, true, i + 1, r);
				res += p(str, false, l, i - 1) * p(str, false, i + 1, r);
				break;
			}
		}
	}
	return res;
}

int num1(string str, bool desired)
{
	if (str.size() < 1 || str == "")
		return 0;
	if (!isValid(str))
		return 0;
	return p(str, desired, 0, str.size() - 1);
}

int num2(string str, bool desired)
{
	if (str.size() < 1 || str == "")
		return 0;
	if (!isValid(str))
		return 0;
	int len = str.size();
	vector<vector<int>> t(len, vector<int>(len, 0));
	vector<vector<int>> f(len, vector<int>(len, 0));
	t[0][0] = str[0] == '0' ? 0 : 1;
	f[0][0] = str[0] == '1' ? 0 : 1;
	for (int i = 2; i < len; i += 2)
	{
		t[i][i] = str[i] == '0' ? 0 : 1;
		f[i][i] = str[i] == '1' ? 0 : 1;
		for (int j = i - 2; j >= 0; j -= 2)
		{
			for (int k = j; k < i; k += 2)
			{
				if (str[k + 1] == '&')
				{
					t[j][i] += t[j][k] * t[k + 2][i];
					f[j][i] += (f[j][k] + t[j][k]) * f[k + 2][i] + f[j][k] * t[k + 2][i];
				}
				else if (str[k + 1] == '|')
				{
					t[j][i] += (f[j][k] + t[j][k]) * t[k + 2][i] + t[j][k] * f[k + 2][i];
					t[j][i] += f[j][k] * f[k + 2][i];
				}
				else
				{
					t[j][i] += f[j][k] * t[k + 2][i] + t[j][k] * f[k + 2][i];
					f[j][i] += f[j][k] * f[k + 2][i] + t[j][k] * t[k + 2][i];
				}
			}
		}
	}
	return desired ? t[0][len - 1] : f[0][len - 1];
}

int main()
{
	string s;
	getline(cin, s);
	bool des = false;
	int res1 = num1(s, des);
	int res2 = num2(s, des);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

排成一条线的纸牌博弈问题

题目

给定一个整形数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。

代码

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

int s(int arr[], int i, int j);
int f(int arr[], int i, int j);

int s(int arr[], int i, int j)
{
	if (i == j)
		return 0;
	return min(f(arr, i + 1, j), f(arr, i, j - 1));
}

int f(int arr[], int i, int j)
{
	if (i == j)
		return arr[i];
	return max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
}

int win1(int arr[], int len)
{
	if (len == 0)
		return 0;
	return max(f(arr, 0, len - 1), s(arr, 0, len - 1));
}

/*递归的方法导致时间复杂度达到O(2^n),空间复杂度为O(n);
动态规划生成两个矩阵f,s,f[i][j]表示函数f(i, j)的返回值,s[i][j]表示
函数是是s(i, j)的返回值*/

int win2(int arr[], int len)
{
	if (len < 1)
		return 0;
	vector<vector<int>> f(len, vector<int>(len, 0));
	vector<vector<int>> s(len, vector<int>(len, 0));
	for (int j = 0; j < len; j++)
	{
		f[j][j] = arr[j];
		for (int i = j - 1; i >= 0; i--)
		{
			f[i][j] = max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
			s[i][j] = min(f[i + 1][j], f[i][j - 1]);
		}
	}
	return max(f[0][len - 1], s[0][len - 1]);
}

int main()
{
	int n; 
	cin >> n;
	int* in = new int[n];
	for (int i = 0; i < n; i++)
	{
		cin >> in[i];
	}
	int res1 = win1(in, n);
	int res2 = win2(in, n);
	cout << res1 << " " << res2 << endl;
	getchar();
	return 0;
}

跳跃游戏

题目

给定数组arr,arr[i]==k代表可从位置i向右跳1-k个距离。比如arr[2]==3,代表从位置2可以跳到位置3或4或5.如果从位置0出发,返回最少跳几次能跳到arr最后的位置上。
要求:如果arr长度为N,要求实现时间复杂度为O(N),额外空间复杂度为*O(1)*的方法。

代码

int jumpGame(int arr[], int len)
{
	if (len < 1)
		return -1;
	int cur = 0;
	int next = 0;
	int step = 0;
	for (int i = 0; i < len; i++)
	{
		if (cur < i)
		{
			step++;
			cur = next;
		}
		next = max(next, i + arr[i]);
	}
	return step;
}

数组中的最长连续序列

题目

给定无序数组arr,返回其中最长的连续序列的长度。

代码

使用哈希表可以实现时间复杂度为O(N)、额外空间复杂度为O(N)的方法。

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

int merge(map<int, int> &mp, int less, int more)
{
	int left = less - mp[less] + 1;
	int right = more + mp[more] - 1;
	int len = right - left + 1;
	mp[left] = len;
	mp[right] = len;
	return len;
}

int maxConsecutive(int arr[], int len)
{
	if (len < 1)
		return 0;
	int maxLen = 1;
	map<int, int> m;
	for (int i = 0; i < len; i++)
	{
		if (m.find(arr[i]) == m.end())
		{
			m.insert(pair<int, int>(arr[i], 1));
			if (m.find(arr[i] - 1) != m.end())
			{
				int tmp1 = merge(m, arr[i] - 1, arr[i]);
				maxLen = max(maxLen, tmp1);
			}
			if (m.find(arr[i] + 1) != m.end())
			{
				int tmp2 = merge(m, arr[i], arr[i] + 1);
				maxLen = max(maxLen, tmp2);
			}
		}
	}
	return maxLen;
}
int main()
{
	int len;
	cin >> len;
	int* arr = new int[len];
	for (int i = 0; i < len; i++)
		cin >> arr[i];
	int maxLen = maxConsecutive(arr, len);
	delete[] arr;
	cout << maxLen << endl;
	getchar();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值