淼淼刷力扣(PTA特别版)

引言

本人初次尝试写博客,希望各位看官大佬多多包容
有错误希望巨巨们提出来,我一定会及时改正,谢谢大家
在自己最好的年纪,找到了未来的目标
还有1年奋斗刷题,明年去面试实习,加油!
博主今天学校上机实验,给大家分享一下

题目一要求:

注:
1、本体由于老师问题,导致题目不严谨,只能采用从后向前的顺序才能解出来,从前到后顺序会错
2、题目没说明白如果多个答案情况,是怎么取舍问题,所以暂时不理会

在这里插入图片描述

整体思路

1、我们类比一个场景:一辆汽车排队进入,要求恰好进入一些人之后重量为m,给了n个排队的人的重量
第一:这么一堆人,为了让我的处理更具有条理性,所以我们必须按照先后次序依次处理,产生了一个类似于链表的递归链条,。
第二:我们随便来一个人,体重大于要求了,他则必定不行,看下一个(前提得判断有下一位),这个人一上,恰好达到标准重量,则之前选中的人和他自己都是目标了,则返回true,告诉他们的前辈们,若体重小于要求,由于01背包要求恰好装入,所以可能之后恰好装入,也可能不装入,,所以这样,一旦发现恰好装入,就集体返回true,所有这条路上的接到true就代表满足题意,接着返回true。
第三:大家都在一个递归的大结构上,就算你不是要的节点,但是既然在这条路上了,就要担负起传递信息的任务。
2、结合上面的场景,一置换就是我们的01背包问题!

具体代码(内附注释):

注:
1、返回true的路径就是能装进去的,不能装的也要帮着传递信息
2、由于题目缺陷只能从后向前处理。
3、回溯法的一个核心思想就是要把底层的信息带到上面去,每一个人都是传话筒,把底下信息传上去,达到回溯效果。

//本算法是从后向前处理
#include <iostream>
#include <vector>
using namespace std;
vector<int >ans, item;
bool find(int m, int n) {//核心算法,m为重量,n是处理的是谁
	int temp = item[n];//获得当前重量,之后可能有三种情况出现。
	if (temp > m) {//情况一
		if (n <= 0) {//到了最后一个还是装不进去,一定错了
			return false;
		}
		return find(m, n - 1);//没到最后,既然他自己不行,就按顺序处理下一个,但他要把之后处理的啥样向上传递
		//这就是所谓的回溯法,要把底下的信息带到上面去。
	}
	if (temp == m) {//情况二,恰好装入
		ans.push_back(temp);
		return true;//所有返回true就代表了可以装入
	}
	if (temp < m) {
		if (n <= 0) {//到最后了不能装入就是错
			return false;
		}
		//看看他装进去了,后面能不能恰好装入
		if (find(m - temp, n - 1)) {//能的话,后面给他true,他就知道了自己装进去是对的,接着返回true,告诉上面
			ans.push_back(temp);
			return true;
		}
		else {
			return find(m, n - 1);//说明这个点不能放进去,接着处理下一个
		}
	}
}
int main() {
	int n, m;
	scanf("%d%d", &m, &n);
	for (int i = 0; i < n; i++) {
		int temp;
		scanf("%d", &temp);
		item.push_back(temp);
	}
	vector<int> path;
	find(m, n - 1);
	if (ans.size() == 0) {//以下是控制输出,可不看
		cout << 0;
		return 0;
	}
	int i = 0;
	for (; i < ans.size() - 1; i++) {
		printf("%d ", ans[i]);
	}
	printf("%d", ans[i]);
	return 0;
}

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短-时间):

在这里插入图片描述

题目二要求:

在这里插入图片描述

整体思路

就是归并排序,具体解析见我另一篇博客归并排序

具体代码(内附注释):

#include <iostream>
#include <vector>
using namespace std;
void apart(vector<int>& A, int i, int j) {//典型的回溯合并,向下分解
	if (i >= j) {//终止条件
		return;
	}
	int mid = (i + j) / 2;
	apart(A, i, mid);//后根,这个函数一完事,i-mid排完了
	apart(A, mid + 1, j);//这个函数一完事,mid+1-j排完了
	//写之后的融合代码的时候,可以找一个简单的样例写,再拿边界情况验证
	//辅助vector
	//走到这里的代表i-mid 和 mid+1-j 都已经整好了,剩下的就是把这个两个整体合并一下就行了,先存到辅助数组里,在复制即可
	//上下级传递的是指针
	//下面就是排i-j
	vector<int> temp(j - i + 1, 0);
	int left = i;//left从i到mid,right从mid+1到j
	int right = mid + 1;
	int point = 0;
	while (left <= mid && right <= j) {
		if (A[left] <= A[right]) {
			temp[point++] = A[left++];//看谁小谁就下去放进辅助数组
		}
		else {
			temp[point++] = A[right++];
		}
	}
	while (left <= mid) {//把剩下的放进去
		temp[point++] = A[left++];
	}
	while (right <= j) {
		temp[point++] = A[right++];
	}
	left = i;
	point = 0;
	while (left <= j) {
		A[left++] = temp[point++];
	}
}
int main() {
	vector<int> A;
	int num;
	scanf("%d", &num);
	for (int i = 0; i < num; i++) {
		int temp;
		scanf("%d", &temp);
		A.push_back(temp);
	}
	apart(A, 0, A.size() - 1);
	int i = 0;
	for (; i < A.size() - 1; i++) {
		printf("%d ", A[i]);
	}
	printf("%d", A[i]);
}

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短-时间):

在这里插入图片描述

题目三要求:

在这里插入图片描述

整体思路

明确目标:这个题其实我们要抓住题目,题目说t时刻t水深,我们就看这个水深能不能让我们游到终点,能的话这一定是一个解,不能的话就说明太浅了,所以总体思路就是给水深,看能不能到终点就行了。

具体代码(内附注释):

注:
1、一定不要忽视原点处水深,根据题意“ T=t 时想从(i,j)位置移动到(i,j+1)位置,需要满足t≥dungeon[i][j]且t≥dungeon[i][j+1]”说明想从源点离开,水深必须得达到原点的深度,所以将答案的初始值设置为原点水深。
2、一定注意输入格式,像我采用的方法直接一个string读入,那么我们就要想到特别处理10,188,等位数多得数
3、对于find函数再次说明:一定要清楚,我这里是并没有考虑原点的深度的,假设一定可以从原点出去。看当前节点能不能到终点,能,则前驱节点更新为该节点,向前再找一个节点作为当前节点,不能,说明前驱节点是最小深度。并且得到的答案一定要反过来考虑这个深度能不能游出原点,所以和初始值二者选大者是答案。注意:此时temp是深度非递减序列,实验过后发现就算循环走完了发现任意深度都能走到终点,那么就意味着假设能从原点走出来,任意深度都能走到终点,那么答案就是初始值,即保证能走出原点。
4、对全局变量"bool isVisted[10000][10000];"说明:他记录任意一次dfs经过的节点的数组,经过了是true,但一定要注意一旦这个点处理完了就必须把这个点的状态复原,一定要记住这个操作,因为状态数组是全局的!!!!,并且记住一个技巧,在设置状态时,只设置当前层的状态,不跨层设置。

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
using namespace std;
vector<int > temp;
int item[10000][10000];//存储各个点水深
bool isVisted[10000][10000];//存储每条路径经过情况,一条路径相同的点不可重复走
int ans_time = 0;//答案
int hlong = 0;//规模,也就是N*N中的N
int dx[] = { -1,0,1,0 };//二者共同组成4方向
int dy[] = { 0,-1,0,1 };
int max(int a, int b) {//自己写的找最大函数
	return a > b ? a : b;
}
bool time(int i, int j, int max) {//看能不能到终点
	isVisted[i][j] = true;//上来就要标记状态
	bool flag = false;
	if (i == hlong - 1 && j == hlong - 1) {//已经找到终点
		isVisted[i][j] = false;//复原状态
		return true;//回溯传递信息
	}
	for (int k = 0; k < 4; k++) {
		int x = i + dx[k];
		int y = j + dy[k];
		if ((x < 0 || x >= hlong) || (y < 0 || y >= hlong) || isVisted[x][y] == true || item[x][y] > max) {
			continue;
		}//判断条件:不可越界+这个点没走过+深度不可以超过我给的最大值
		flag = time(x, y, max);//走到这里说明x,y可以走,可以继续判断
		if (flag) {//快速退出通道
			isVisted[i][j] = false;//快速退出就把状态复原
			return true;
		}
	}
	isVisted[i][j] = false;//处理完退出就把状态复原
	return false;//一定要手动返回值,要不默认返回true
}
void find(int i, int j) {
	int num = temp.size();//初始化前驱
	int last = temp[num - 1];//从倒数第二个开始判断。
	//一旦当前的深度走不了,说明前驱就是答案,要不然前驱节点就是本节点,处理的节点向前一个
	int k = num - 2;
	for (; k >= 0; k--) {//这里并没有处理原点值
		if (time(i, j, temp[k])) {
			last = temp[k];
		}
		else {
			ans_time = max(ans_time, last);//最后得到的值和原点值取最大,保证能从原点走出去。
			break;
		}
	}
	
}
int main() {
	string str1;
	std::cin >> str1;
	int temp_integer = 0;//记录数字数值
	bool flag = false;//代表开始拼树字
	for (int i = 0; i < str1.size(); i++) {//处理输入,说白了就是一个字符一个字符处理就行
		if (str1[i] >= '0'&&str1[i] <= '9') {
			int tmp = str1[i] - '0';
			temp_integer = temp_integer * 10 + tmp;//拼树字
			flag = true;
		}
		else {
			if (flag) {
				temp.push_back(temp_integer);
				temp_integer = 0;//在遇到数字后第一次遇到字符,代表这个数字拼完了,可以压入
				flag = false;
			}
		}
	}
	ans_time = temp[0];//做初始化,因为我找水深时候没考虑原点,所以初始化就把原点考虑进去,
	//在之后后面的最深深度和原点取最大值即可
 	hlong = sqrt(temp.size());
	for (int i = 0; i < temp.size(); i++) {
		int x = i / hlong;//找位置
		int y = i % hlong;
		item[x][y] = temp[i];
	}
	sort(temp.begin(), temp.begin() + temp.size());//排序,方便从后面找最深水深
	find(0, 0);
	printf("%d", ans_time);
}//[[8,3,4],[5,6,8],[9,7,3]]

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短-时间):

在这里插入图片描述

题目四要求:

在这里插入图片描述
在这里插入图片描述

整体思路

1、这道题,我的理解里就是一个分治回溯过程,将一个大的任务一步一步的分解成若干小任务,等到小任务都完事了,大任务再换一种分解方法,这里的限制就是要求的分解队列必须非递减,这个限制了我们分解方法
2、先打印再分解。
3、具体过程如图所示:

在这里插入图片描述

具体代码(内附注释):

注:一定要注意防止越界,因为我要的序列要保证非递减,所以所以我再分解的时候我分解出来的数就要和他上一位比,所以在开始的时候就一定要保证-1不越界,所以一切从1开始!!!!!!!

#include <iostream>
#include <vector>
using namespace std;
int a[10000];//存储数组
void print(int t) {//给位置直接打印就行
	int i = 1;
	for (; i < t; i++) {
		printf("%d+", a[i]);
	}
	printf("%d", a[i]);
	printf("\n");
}
void dfs(int N, int t) {
	for (int i = 1; i <= N / 2; i++) {
		if (i >= a[t - 1]) {//保证了非递减序列
			a[t] = i;//把这一种分解方式放进自己对应在a的位置
			a[t + 1] = N - i;
			print(t + 1);//打印,每次把t,t+1放进去,前面是已经排好了的,所以直接从1到t+1打印就行
			dfs(N - i, t + 1);//然后把分解后的数接着发给下一个函数去分解
			//等下面的数都分解好了,再回溯过来,把这个函数的N换一种方式再次分解
			//等分好了,那么这个数就完事返回告诉他的父亲节点就行了,他父节点就接着按照别的方法分解
		}
	}
}
int main()
{
	int n;
	scanf("%d", &n);
	dfs(n, 1);
	return 0;
}

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短-时间):

在这里插入图片描述

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLU_LYM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值