华东师范大学数据学院2020夏令营机试题解

华东师范大学数据科学与工程学院第八届“云计算与大数据”夏令营机试时间是8:30-11:30,共6道题,在陆老师负责的学院自己的OJ平台上进行。OJ平台使用体验良好。

A.排序

给一串整数,有些数字是0,把0换成正整数,是的整个数组非递减且字典序最小。
输入:第一行一个整数n,表示数组元素个数;第二行n个整数。
输出:替换0之后的非递减数组,或者无解输出-1。
样例输入:

5
0 0 1 2 0

样例输出:

1 1 1 2 2

思路:因为要字典序最小,贪心,每一个0都让其等于前一个数的值,需要考虑第一个数是0的情况,第一个数要换成1;还有无解的情况,出现左边的数大于右边的数。(我这个2个情况一开始都没考虑,WA了2次)
AC代码:

#include 
#include 
using namespace std;
int n, a[1005];
int main() {
	freopen("排序.txt", "r", stdin);
	scanf("%d", &n);
	bool f = true;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		if (a[i-1] > a[i] && a[i] != 0) {
			f = false;
			break;
		}
		if (i == 1 && a[i] == 0) a[i] = 1;
		if (a[i] == 0) {
			a[i] = a[i-1];
		}
	}
	if (f == false) {
		printf("-1\n");
		return 0;
	}
	for (int i = 1; i <= n; i++) {
		printf("%d%c", a[i], i ==n?'\n':' ');
	}
	return 0;
}

B.选择运算

初始数为0,每次2个+ - * / 的操作,选择一个,最后得到的数要最大。
样例输入:

3
+1 *3
+2 *40
-3 /2

样例输出:

37

样例解释:第一次选+1,0+1=1;第二次选*40,1*40=40;第三次选-3,40-3=37
数据范围:操作数的范围是1-10的整数
思路:贪心,每次选运算之后结果最大的,需要注意的就是字符串读入的处理。
代码:

#include 
#include 
#include 
using namespace std;
int oper(int x, int y, char op) {
	if (op == '*') return x*y;
	if (op == '+') return x+y;
	if (op == '-') return x-y;
	if (op == '/') return x/y;
	return 0; 
}
int n; 
int main() {
	freopen("选择运算.txt", "r", stdin);
	scanf("%d", &n);
	string a, b;
	int cur = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a >> b;
		int aa = 0;
		if (a.size() == 2) {
			aa = a[1]-'0';
		} else {
			aa = (a[1]-'0')*10+a[2]-'0';
		}
		int bb = 0;
		if (a.size() == 2) {
			bb = b[1]-'0';
		} else {
			bb = (b[1]-'0')*10+b[2]-'0';
		}
		cur = max(oper(cur, aa, a[0]), oper(cur, bb, b[0]));
	}
	printf("%d\n", cur);
	return 0;
}

上面的代码应该是能AC的,但我AC不是提交的上面的代码,当时因为一个变量写错了一直WA,担心贪心得不到最优解,使用了递归把所有的运算都走了一遍(数据比较弱)
AC代码:

#include 
#include 
#include 
#include  
using namespace std;
int oper(int x, int y, char op) {
	if (op == '*') return x*y;
	if (op == '+') return x+y;
	if (op == '-') return x-y;
	if (op == '/') return x/y;
	return 0; 
}
int n; 
vector<int> na;
vector<int> nb;
vector<char> oa;
vector<char> ob;
const int INF = 0x3f3f3f3f;
int ans = -INF;
void dfs(int cur, int id) {
	if (id == n) {
		ans = max(ans, cur);
		return;
	}
	int t = oper(cur, na[id], oa[id]);
	dfs(t, id+1);
	t = oper(cur, nb[id], ob[id]);
	dfs(t, id+1);
}
int main() {
	freopen("选择运算.txt", "r", stdin);
	scanf("%d", &n);
	string a, b;
	int cur = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a >> b;
		int aa = 0;
		if (a.size() == 2) {
			aa = a[1]-'0';
		} else {
			aa = (a[1]-'0')*10+a[2]-'0';
		}
		na.push_back(aa);
		oa.push_back(a[0]);
		int bb = 0;
		if (b.size() == 2) {
			bb = b[1]-'0';
		} else {
			bb = (b[1]-'0')*10+b[2]-'0';
		}
		nb.push_back(bb);
		ob.push_back(b[0]);
	}
	dfs(0, 0);
	printf("%d\n", ans);
	return 0;
}

C.保险箱

给定n和m,求0—(n-1)这n个数从小到大全排列的第m个。
样例输入:

4 3

样例输出:

0213

样例解释:由0-3组成的全排列依次有:0123、0132、0213、0231、0312、0321……第3个数是0213
思路:直接暴力递归(再次证明数据规模不大),需要注意0在最前面的情况。
AC代码:

#include 
#include 
using namespace std;
typedef long long ll;
int n, m;
bool vis[11], fi = false;
ll ans = 0, cnt = 0;
void dfs(int id, ll cur) {
	if (fi == true) {
		return;
	}
	if (id == n) {
		cnt++;
	}
	if (cnt == m) {
		fi = true;
		ans = cur;
		return;
	}
	
	for (int i = 0; i < n; i++) {
		if (vis[i] == false) {
			vis[i] = true;
			dfs(id+1, cur*10+i);
			vis[i] = false;
		}
	}
}
int main() {
	scanf("%d%d", &n, &m);
	ll cur = 0;
	dfs(0, cur);
	ll t = 1;
	for (int i = 1; i < n; i++) {
		t *= 10;
	}
	if (ans < t) {
		printf("0");
	}
	printf("%lld\n", ans);
	return 0;
} 

D.二分图

判断给定的无向图是不是二分图
输入:第一行一个数表示顶点个数n,下面n行,每行若干个数,第 i i i行表示与顶点 i i i相邻的顶点,以 − 1 -1 1结束。
样例输入:

4
1 3 -1
0 2 -1
1 3 -1
0 2 -1

样例输出:

true

思路:dfs的应用二分图匹配
AC代码:

#include 
#include 
#include 
using namespace std;
const int N = 1005;
vector<int> g[N];
int n;
int color[N];
bool ans = true;
void dfs(int x, int c) {
	if (ans == false) return ;
	color[x] = c;
	for (int i = 0; i < g[x].size(); i++) {
		int y = g[x][i];
		if (color[y] == c) {
			ans = false;
			return;
		}
		if (color[y] == 0)
			dfs(y, -c);
	}
}
int main() {
	freopen("二分图.txt", "r", stdin);
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		int t = 0;
		while (1) {
			scanf("%d", &t);
			if (t == -1) break;
			g[i].push_back(t);
		}
	}
	for (int i = 0; i < n; i++) {
		if (color[i] == 0) {
			dfs(i, 1);
		}
	}
	if (ans) printf("true\n");
	else printf("false\n");
	return 0;
} 

E.分割数组

给一个长度为n的数组,划分成m个不为空的子数组,m个子数组中元素和最大的定义为这种划分方式的值,求所有划分方式的最小值
输入:第一行2个数,n和m;第二行n个数
输出:所有划分方式的最小值
样例输入:

5 2
7 2 5 10 8

样例输出:

18

样例解释:划分成2个数组,比如 [ 7 ] [7] [7] [ 2 , 3 , 10 , 8 ] [2,3,10,8] [2,3,10,8],这种划分的值是2+3+10+8=23;或者 [ 7 , 2 ] [7,2] [7,2] [ 3 , 10 , 8 ] [3,10,8] [3,10,8],这种划分的值是 m a x ( 7 + 2 , 3 + 10 + 8 ) = 21 max(7+2,3+10+8)=21 max(7+2,3+10+8)=21,输出的是所有划分的最小值。
思路:还是暴力,把所有划分情况枚举出来,得到每个划分的值,然后求最小值(比较菜,只会暴力)需要注意 i n t int int型会WA,我开了 l o n g long long l o n g long long
AC代码:

#include 
#include 
#include  
using namespace std;
typedef long long ll;
const int N = 1005;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll a[N], sum[N];
int n, m;
ll ans = INF;
void dfs(vector<int> t, int id) {
	if (t.size() == m-1) {
		ll ma = 0;
		int left = 0, right = 0;
		for (int i = 0; i <= m-2; i++) {
			right = t[i];
			ma = max(ma, sum[right]-sum[left]);
			left = right;
		}
		ma = max(ma, sum[n] - sum[right]);
		ans = min(ma, ans);
		return ;
	}
	for (int i = id+1; i <= n-1; i++) {
		dfs(t, i);
		t.push_back(i);
		dfs(t, i);
	}
}
int main() {
	freopen("分割数组.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		sum[i] = sum[i-1]+a[i];
	}
	vector<int> t;
	dfs(t, 0);
	printf("%lld\n", ans);
	return 0;
}

F.二叉树

给一个二叉树,每个节点的值是0或者1,删除所有子树节点值为0的的节点,输出删除之后的二叉树
输入:第一行一个整数表示层次遍历二叉树的结点的值,-1表示该节点为空
输出:删除之后层次遍历二叉树的结果
样例输入:

15
1 0 1 1 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1

样例输入:

1 0 1 1 -1 -1 -1 -1 -1

样例解释:
华东师范大学数据学院2020夏令营机试题解_第1张图片
思路:dfs,如果当前节点及其子树节点的值全部为0,标记一下,之后层次遍历输出的时候判断标记即可
AC代码:

#include 
#include 
#include 
using namespace std;
const int N = 10005;
struct Node {
	int left, right, val;
} a[N]; 
int cnt = 0, n;
bool vis[N];
int dfs(int x) {
	if (a[x].val == -1) return 0;
	int t = a[x].val;
	t += dfs(a[x].left);
	t += dfs(a[x].right);
	if (t == 0) vis[x] = false;
	return t;
}
int main() {
	freopen("二叉树.txt", "r", stdin);
	scanf("%d", &n);
	int t;
	queue<int> q;
	q.push(0);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &t);
		int x = q.front(); q.pop();
		a[x].val = t;
		if (t != -1) {
			a[x].left = ++cnt;
			a[x].right = ++cnt;
			q.push(a[x].left);
			q.push(a[x].right);
		}
	}
	for (int i = 0; i <= cnt*2; i++) {
		vis[i] = true;
	}
	t = dfs(0);
	if (t == 0) vis[0] = false;
	while (!q.empty()) q.pop();
	if (vis[0] == true)q.push(0);
	while (!q.empty()) {
		int x = q.front();q.pop();
		if (vis[x] == true) printf("%d ", a[x].val);
		else printf("-1 ");
		if (a[x].val == -1 || vis[x] == false) {
			continue;
		}
		q.push(a[x].left);
		q.push(a[x].right);
	}
	return 0;
}

总结

机试的题涉及的知识点都是一些基础的数据结构和算法,如递归,bfs,dfs,二分图等,题目虽然没给数据范围,但数据规模并不大。很多题我提交了几次都WA了,后来再回过头来重新思考AC了,有些代码复制完之后直接提交了,忘了注释,WA了感觉很可惜,所以最后我的罚时还是比较严重的,第一个AC的拿了4个题的第一,差距还是非常大的。

你可能感兴趣的:(题解)