第六周
第一题:
题目来源:3729. 改变数组元素 - AcWing题库
给定一个空数组 V和一个整数数组 a1,a2,…,an。
现在要对数组 V 进行 n次操作。
第 i 次操作的具体流程如下:
- 从数组 V 尾部插入整数 00。
- 将位于数组 V 末尾的 ai 个元素都变为 1(已经是 1 的不予理会)。
注意:
- ai 可能为 0,即不做任何改变。
- ai 可能大于目前数组 V 所包含的元素个数,此时视为将数组内所有元素变为 1。
请你输出所有操作完成后的数组 V。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 n。
第二行包含 n个整数 a1,a2,…,an。
输出格式
每组数据输出一行结果,表示所有操作完成后的数组 V,数组内元素之间用空格隔开。
数据范围
1≤T≤20000,
1≤n≤2×10^5,
0≤ai≤n,
保证一个测试点内所有 n 的和不超过 2×10^5。
输入样例:
3
6
0 3 0 0 1 3
10
0 0 0 1 0 5 0 0 0 2
3
0 0 0
输出样例:
1 1 0 1 1 1
0 1 1 1 1 1 0 0 1 1
0 0 0
解题代码:
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
while (n--) {
int m;
scanf("%d", &m);
int arr[m], ans[m];
for (int i = 0; i < m; i++) scanf("%d", &arr[i]);
int j = m - 1, idx = 0;
while (j >= 0) {
if (!arr[j]) ans[idx++] = 0, j--;
else {
int k = arr[j];
while (1) {
int index = j, end = index - k;
for (j; j > end && j >= 0; j--) {
if (arr[j] > k) {
k = arr[j];
break;
}
else {
ans[idx++] = 1;
}
k--;
}
if (j < 0 || j == end) {
break;
}
}
}
}
for (int i = idx - 1; i >= 0; i--) printf("%d ", ans[i]);
printf("\n");
}
return 0;
}
解题思路:
我自己也不知道这是什么算法,但可以肯定的是它的时间复杂度是o(n),为什么呢,因为本题实际上只用了一个尾指针向前扫描,并且该指针没有回溯,因此时间复杂度应该是扫描n个一遍的时间,所以o(n),大概思路是:我们可以知道,它后面的操作会覆盖掉前面的操作,而前面一定不会影响到后面,那我们从后往前扫描,最后反转结果就行,如果某一位非0为n,那么从此位起至前n位一定为1,不用看,只需要处理其中大于n的数,及时更新就行。
第二题:
题目来源:1497. 树的遍历 - AcWing题库
题目描述:
一个二叉树,树中每个节点的权值互不相同。现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。
输入格式
第一行包含整数 N,表示二叉树的节点数。
第二行包含 N 个整数,表示二叉树的后序遍历。
第三行包含 N 个整数,表示二叉树的中序遍历。
输出格式
输出一行 N个整数,表示二叉树的层序遍历。
数据范围
1≤N≤30
官方并未给出各节点权值的取值范围,为方便起见,在本网站范围取为 1~N。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
4 1 6 3 5 7 2
解题代码:
#include <stdio.h>
#include <stdlib.h>
#define N 60
struct tree {
int data;
struct tree* lchild;
struct tree* rchild;
};
struct queue {
struct tree* data[N];
int front;
int rear;
};
int postIndex;
int find(int data, int arr[], int start, int end) {
int i;
for (i = start; i <= end; i++) {
if (arr[i] == data)
return i;
}
}
struct tree* createtree(int *postorder,int *inorder,int begin,int end) {
if (begin > end) {
return NULL;
}
struct tree *node = (struct tree*)malloc(sizeof(struct tree));
node->data = postorder[postIndex--];
node->lchild = NULL;
node->rchild = NULL;
int idx = find(node->data, inorder, begin, end);
node->rchild = createtree(postorder, inorder, idx + 1, end);
node->lchild = createtree(postorder, inorder, begin, idx - 1);
return node;
}
void leverorder(struct tree* p) {
struct queue q;
q.front = 0;
q.rear = 0;
if (p != NULL) q.data[q.rear++] = p;
while (q.front != q.rear) {
struct tree* node = q.data[q.front++];
printf("%d ", node->data);
if (node->lchild != NULL) q.data[q.rear++] = node->lchild;
if (node->rchild != NULL) q.data[q.rear++] = node->rchild;
}
}
int main() {
int n;
scanf("%d",&n);
int postorder[N];
int inorder[N];
for (int i = 0; i < n; i++) {
scanf("%d",&postorder[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d",&inorder[i]);
}
postIndex = n - 1;
struct tree *root = createtree(postorder,inorder,0,n-1);
leverorder(root);
return 0;
}
解题思路:
非常暴力了,一种很简单的思路,就是利用两种遍历结果,真正构造一棵二叉树,然后再用队列进行层序遍历即可,我们很容易知道,对于后序遍历,那么它最后一个节点一定是根节点,那我们我们可以在中序遍历中找到这个根节点,并且中序遍历的顺序是左中右,那么在该节点左边就是左子树,在它右边就是右子树,对于左子树而言,在后序遍历中,左子树的那一部分的最后一个节点又是左子树的根节点,即可以用递归的方式建立一棵二叉树,就自然而然能够进行层序遍历了。
第三题:
题目来源:1460. 我在哪? - AcWing题库
题目描述:
农夫约翰出门沿着马路散步,但是他现在发现自己可能迷路了!
沿路有一排共 N个农场。
不幸的是农场并没有编号,这使得约翰难以分辨他在这条路上所处的位置。
然而,每个农场都沿路设有一个彩色的邮箱,所以约翰希望能够通过查看最近的几个邮箱的颜色来唯一确定他所在的位置。
每个邮箱的颜色用 A…Z之间的一个字母来指定,所以沿着道路的 N个邮箱的序列可以用一个长为 N的由字母 A…Z组成的字符串来表示。
某些邮箱可能会有相同的颜色。
约翰想要知道最小的 K 的值,使得他查看任意连续 K个邮箱序列,他都可以唯一确定这一序列在道路上的位置。
例如,假设沿路的邮箱序列为 ABCDABC
。
约翰不能令 K=3,因为如果他看到了 ABC
,则沿路有两个这一连续颜色序列可能所在的位置。
最小可行的 K的值为 K=4,因为如果他查看任意连续 4 个邮箱,那么可得到的连续颜色序列可以唯一确定他在道路上的位置。
输入格式
输入的第一行包含 N,第二行包含一个由 N个字符组成的字符串,每个字符均在 A…Z 之内。
输出格式
输出一行,包含一个整数,为可以解决农夫约翰的问题的最小 K值。
数据范围
1≤N≤100
输入样例:
7
ABCDABC
输出样例:
4
解题代码:
一:
#include <stdio.h>
int main() {
int n;
char str[1001];
scanf("%d\n", &n);
gets(str);
for (int k = 1; k <= n; k++) {
int idx = 0;
for (int i = 0; i + k - 1 < n; i++) {
for (int j = i + 1; j + k - 1 < n; j++) {
int index = 1;
for (int q = 0; q < k; q++){
if (str[i + q] != str[q + j]) {
index = 0;//不相同
break;
}
}
if (index) {
idx = 1;//相同
break;
}
}
if (idx) {
break;
}
}
if (!idx) {
printf("%d", k);
return 0;
}
}
return 0;
}
二:
#include <stdio.h>
char str[1000];
int n;
int check(int k,int idx) {
for (int i = 0; i + k - 1 < n; i++) {
for (int j = i + 1; j + k - 1 < n; j++) {
int same = 1;
for (int m = 0; m < k; m++) {
if (str[m + i] != str[m + j]) {
same = 0;
break;
}
}
if (same) {
idx = 0;
break;
}
}
}
return idx;
}
int main() {
scanf("%d\n", &n);
gets(str);
int l = 1, r = n;
while (l<r) {
int mid = l + r >> 1;
if (check(mid,1)) {
r = mid;
}
else {
l = mid+1;
}
}
printf("%d", l);
return 0;
}
三:
#include <iostream>
#include <cstring>
#include <unordered_set>
using namespace std;
int n;
string str;
bool check(int mid) {
unordered_set<string>hash;
for (int i = 0; i + mid - 1 < n; i++) {
if (hash.count(str.substr(i, mid))) {
return false;
}
hash.insert(str.substr(i, mid));
}
return true;
}
int main() {
cin >> n >> str;
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
}
else {
l = mid + 1;
}
}
cout << r << endl;
return 0;
}
解题思路:
用了三种方法来做这个题,第一种就是经典的暴力,时间复杂度大概是n的四次方,第二种做法则是用了一层二分查找,我们可以知道,如果k是正确答案,那么k到n一定也是正确答案,1到k一定不是正确答案,符合二段性,所以可以用二分,大概时间复杂度是n的三次方的logN次方,第三种方法,用到了一个哈希表,通过对字符串是否存在的查询,再加上二分的搜索,使时间复杂度控制在N的logN次方。
第四题:
题目来源:P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
由数字 0组成的方阵中,有一任意形状的由数字 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2。例如:6×6的方阵(n=6),涂色前和涂色后的方阵如下:
如果从某个 0 出发,只向上下左右 4 个方向移动且仅经过其他 0 的情况下,无法到达方阵的边界,就认为这个 0 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内的 0 是连通的(两两之间可以相互到达)。
0 0 0 0 0 0
0 0 0 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 1 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 0 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 1 2 1
1 1 1 1 1 1
输入格式
每组测试数据第一行一个整数 n(1≤n≤30)。
接下来 n 行,由 0 和 1组成的 n×n 的方阵。
方阵内只有一个闭合圈,圈内至少有一个 0。
输出格式
已经填好数字 2 的完整方阵。
输入输出样例
**输入 **
6
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
**输出 **
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1
说明/提示
对于 100 的数据,1≤n≤30
解题代码:
#include <stdio.h>
int x_m[4] = { 0,0,1,-1 };
int y_m[4] = { 1,-1,0,0 };
int n;
int arr[31][31];
void search(int x,int y) {
arr[x][y] = 3;
for (int k = 0; k < 4; k++) {
int x0 = x + x_m[k];
int y0 = y + y_m[k];
if (x0 >= 0 && x0 < n && y0 >= 0 && y0 < n && arr[x0][y0] == 0) {
search(x0, y0);
}
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &arr[i][j]);
}
}
for (int i = 0; i < n; i++) {
if (arr[i][0] == 0) {
search(i, 0);
}
if (arr[i][n - 1] == 0) {
search(i, n - 1);
}
}
for (int j = 0; j < n; j++) {
if (arr[0][j] == 0) {
search(0, j);
}
if (arr[n - 1][j]==0) {
search(n - 1, j);
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (arr[i][j] == 0) {
arr[i][j] = 2;
}
if (arr[i][j] == 3) {
arr[i][j] = 0;
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
解题思路:
- 变量定义:
x_m
和y_m
是用于在四个方向(上、下、左、右)上移动的数组。n
是二维数组arr
的大小,arr
用于存储输入的二维网格。 - 搜索函数:
search
函数是一个递归函数,用于执行深度优先搜索。它首先将当前位置标记为3
,然后在四个方向上进行搜索。如果新的位置在网格内并且值为0
,则对新位置进行搜索。 - 主函数:
main
函数首先读取网格的大小n
,然后读取n*n
的网格。然后,它在四个边缘上的每个位置开始搜索,如果该位置的值为0
,则调用search
函数。最后,它遍历整个网格,将所有未被访问的位置(值为0
)标记为2
,并将所有被访问的位置(值为3
)标记回0
。然后,它打印出最后的网格。
用了染色法,大致思路如下,已知被1围成一圈的0要改为2,不妨把没有被1围住的零先改成3,然后最后遍历一次,把剩下的零0改成2,把3改回0,首先,定义上下左右移动的数组,然后从两种讨论,第一种是第一排和最后一排,第二种是第一列和最后一列,然后不断的向里面渗透,即完成染色。
第五题:
题目来源:P1141 01迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述:
有一个仅由数字 0 与 1 组成的 n×n 格迷宫。若你位于一格 0上,那么你可以移动到相邻 4 格中的某一格 1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
输入格式
第一行为两个正整数 n,m。
下面 n 行,每行 n 个字符,字符只可能是 0 或者 1,字符之间没有空格。
接下来 m 行,每行两个用空格分隔的正整数 i,j,对应了迷宫中第 i 行第 j 列的一个格子,询问从这一格开始能移动到多少格。
输出格式
m 行,对于每个询问输出相应答案。
输入输出样例
**输入 **
2 2
01
10
1 1
2 2
**输出 **
4
4
说明/提示
对于样例,所有格子互相可达。
- 对于 20%的数据,n*≤10;
- 对于 40%的数据,n*≤50;
- 对于 50%的数据,m*≤5;
- 对于 60%的数据,n*,m≤100;
- 对于 100%的数据,1≤n≤1000,1≤m≤100000。
解题代码:
#include <stdio.h>
#include <string.h>
int n, m;
int arr[1005][1005];
int state[1005][1005];
int x_m[4] = { 1,-1,0,0 };
int y_m[4] = { 0,0,1,-1 };
int sum = 0;
int ans[1005][1005] = { 0 };
int idx = 0;
void search(int x, int y) {
if (ans[x][y]) {
sum = ans[x][y];
return;
}
sum++;
state[x][y] = idx;
for (int k = 0; k < 4; k++) {
int x0 = x + x_m[k];
int y0 = y + y_m[k];
if (x0 > 0 && x0 <= n && y0 > 0 && y0 <= n && state[x0][y0] != idx && arr[x0][y0] != arr[x][y]) {
search(x0, y0);
}
}
}
void get_ans(int x, int y, int u) {
state[x][y] = idx;
ans[x][y] = u;
for (int k = 0; k < 4; k++) {
int x0 = x + x_m[k];
int y0 = y + y_m[k];
if (x0 > 0 && x0 <= n && y0 > 0 && y0 <= n && state[x0][y0] != idx && arr[x0][y0] != arr[x][y]) {
get_ans(x0, y0,u);
}
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%1d", &arr[i][j]);
}
}
while (m--) {
int i, j;
scanf("%d %d", &i, &j);
if (ans[i][j]) {
printf("%d\n", ans[i][j]);
continue;
}
sum = 0;
idx++;
search(i, j);
int u = sum;
printf("%d\n", sum);
idx++;
get_ans(i, j, u);
}
return 0;
}