2020年天梯赛(题解 + 易错点)

L1-6 吃火锅

没有测试点 测 chi1 huo3 guo1 前后是否有别的数字字母
一行中出现多个,就只用加一次

L1-7 前世档案

看作二进制,y为0,n为1,二进制转十进制

L1-8 刮刮彩票

最后给出的1-8, 如果是列的话还要减去3,而不是直接用给的数字

L2-1 简单计算器

判断0的时候只有除法才判断分子为0,不是所有符号都判断

#include <bits/stdc++.h>
using namespace std;
stack<int> s;
stack<char> t;
int calc(int x, int y, char c) {
	if(c == '+') return x + y;
	else if(c == '-') return x - y;
	else if(c == '*') return x * y;
	else if(c == '/') return x / y;
}
int main() {
	int n, f = 1;
	cin >> n;
	for(int i = 0; i < n; ++i) {
		int x; cin >> x;
		s.push(x);
	}
	for(int i = 1; i < n; ++i) {
		char x; cin >> x;
		t.push(x);
	}
	while(!t.empty()) {
		int y = s.top(); s.pop();
		int x = s.top(); s.pop();
		char c = t.top(); t.pop();
		if(c == '/' && y == 0) {
			f = 0;
			printf("ERROR: %d/0\n", x);
			break;
		}
		s.push(calc(x, y, c));
	}
	if(f) cout << s.top();
	return 0;
}

L2-2 口罩发放

这道题确实超级费时。我当时用了50多分钟写这题,改bug,才得了16分。
身份证号码要求18位,每位都要是数字
身体状况为1的输出顺序,为输入记录的顺序
s会为0!!!,即某天可能一个口罩都不发放。所以要提前判断是否有发放名额,而不是发放完再判断口罩是否小于发放人数。
每天的口罩不用叠加之前未发放完的。即每天都是新的名额。

#include <bits/stdc++.h> 
using namespace std;
struct Node {
	string nick, idnum;
	int tag, h, m, id;
	bool operator<(const Node c) const {
		return h == c.h ? m == c.m ? id < c.id : m < c.m : h < c.h;
	}
}b[1055];
vector<pair<string, string>> v;
unordered_map<string, int> mp;
set<string> st;
int main() {
	int d, p;
	cin >> d >> p;
	for(int i = 1; i <= d; ++i) {
		int t, s, cnt = 0;
		scanf("%d%d", &t, &s);
		for(int j = 0; j < t; ++j) {
			cin >> b[j].nick >> b[j].idnum;
			scanf("%d%d:%d", &b[j].tag, &b[j].h, &b[j].m);
			b[j].id = j;
			int flag = 1;
			for(int i = 0; i < (b[j].idnum).size(); ++i) {
				if(!isdigit(b[j].idnum[i])) {
					flag = 0;
					break;
				}
			}
			if(!flag || (b[j].idnum).size() != 18) {
				j--, t--;
				continue;
			}
			if(b[j].tag == 1 && st.find(b[j].idnum) == st.end()) {
				v.push_back({b[j].nick, b[j].idnum});
				st.insert(b[j].idnum);
			}
		}
		sort(b, b + t);
		for(int j = 0; j < t && cnt < s; ++j) {
			if(mp[b[j].idnum] < i) {
				mp[b[j].idnum] = i + p;
				cout << b[j].nick << " " << b[j].idnum << endl;
				cnt++;
			}
		}
	}
	for(auto i : v) {
		cout << i.first << " " << i.second << endl;
	}
	return 0;
}

L2-3 完全二叉树的层序遍历

比赛时我完全没思路,可能当时太瞌睡了,也可能太菜了。
刚才做了一下,秒想了一个思路,写出来20分。其实是错误的思路,测试点太水了。就是分别找左子树和右子树。但是左右子树不一定对半分,所以是错的。代码下面的注释就是错误样例。
上面的思路改着太麻烦了,然后就想别的思路。正解是按照后序遍历模拟,因为是完全二叉树,所以标记当前下标就可以了。

20分的代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 35;
int a[maxn], cnt = 0, b[maxn];
void dfs(int a[], int len, int idx) {
	if(!len) return ;
	b[idx] = a[len - 1];
	dfs(a, len / 2, idx * 2);
	dfs(a + len / 2, len - len / 2 - 1, idx * 2 + 1);
}
int main() {
    int n, f = 1;
    cin >> n;
    for(int i = 0; i < n; ++i) cin >> a[i];
	dfs(a, n, 1);
	for(int i = 1; i <= n; ++i) {
		if(f) f = 0;
		else putchar(' ');
		cout << b[i];
	}
	return 0;
}
/*
9
91 85 71 2 34 10 15 55 18
10
91 85 71 86 2 34 10 15 55 18
*/

正解

#include <bits/stdc++.h>
using namespace std;
const int maxn = 35;
int a[maxn], b[maxn], cnt = 0;
int n, f = 1;
void dfs(int idx) {
	if(2 * idx <= n) {
		dfs(2 * idx);
		if(2 * idx + 1 <= n)
			dfs(2 * idx + 1);
	}
	b[idx] = a[cnt++];
}
int main() {
    cin >> n;
    for(int i = 0; i < n; ++i) cin >> a[i];
    dfs(1);
    for(int i = 1; i <= n; ++i) {
    	if(f) f = 0;
    	else putchar(' ');
    	cout << b[i];
	}
	return 0;
}

L2-4 网红点打卡攻略

首先判断攻略是否包含了所有网红点,即n是否等于N
判断0和攻略的第一个点和最后一个点是否有路
有路的话,判断每两个点之间是否有通路,判断是否走过即可AC。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
int g[maxn][maxn], a[maxn], vis[maxn];
int main() {
    int n, m, k;
    cin >> n >> m;
	while(m--) {
		int x, y, w;
		cin >> x >> y >> w;
		g[x][y] = g[y][x] = w;
	}
	cin >> k;
	int pos = 0, mx = 0x3f3f3f3f, mxn = 0, cnt = 0;
	while(pos++ < k) {
		int sum = 0, f = 0;
		cin >> m;
		for(int i = 1; i <= m; ++i) cin >> a[i];
		if(m == n) {
			memset(vis, 0 ,sizeof vis);
			if(g[0][a[1]] && g[a[m]][0]) f = 1, sum = g[0][a[1]] + g[a[m]][0];
			vis[0] = vis[a[1]] = 0;
			for(int i = 1; i < m; ++i) {
				if(g[a[i]][a[i + 1]] && !vis[a[i + 1]]) {
					vis[a[i + 1]] = 1;
					sum += g[a[i]][a[i + 1]];
				}
				else {
					f = 0;
	 				break;
				}
			}
		}
		if(f) {
			cnt++;
			if(mx > sum) {
				mx = sum;
				mxn = pos;
			}
		}
	}
	cout << cnt << endl << mxn << " " << mx;
	return 0;
}

L3-1 那就别担心了

题目很容易理解,思路也很容易写出来。
从A开始用dfs来判断是否会到达B。但是要求A开始的任意一条边都要能到达B,终点可能是B能推出来的命题,但是我们不关心,只用到B就行了。
唯一需要注意的是dfs有很多重复的点,如果不记忆的话,会超时的。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
vector<int> g[maxn];
int a, b, n, m, cnt = 0;
int vis[maxn];
pair<bool, int> dfs(int idx) {
	if(idx == b) {
		cnt++;
		return {1, 1};
	}
	if(vis[idx]) {
		cnt += vis[idx];
		return {1, vis[idx]};
	}
	bool flag = 1, ent = 0;
	for(auto i : g[idx]) {
		auto t = dfs(i);
		flag &= t.first;
		vis[idx] += t.second;
		ent = 1;
	}
	return {flag && ent, vis[idx]};
}
int main() {
	cin >> n >> m;
	while(m--) {
		scanf("%d %d", &a, &b);
		g[a].push_back(b);
	}
	cin >> a >> b;
	if(dfs(a).first) {
		printf("%d Yes", cnt);
	}
	else {
		printf("%d No", cnt);
	}
	return 0;
}

L3-2 传送门

模拟
x下标范围到10的5次方,所以应该用x下标存。对每个x存加入传送门的y坐标,保存每个传送门的前一个传送门,和能到达的下一个传送门,根据能到达的下一个传送门O(1)的时间内计算出本x的终点x,再用O(n)的时间来计算和。
初始化,把每个x的0和10的9次方先加入。
期望时间复杂度O(nq)

15分, 超时需要优化,但是 错误的测试点不清楚哪里错了,希望能提供一下答案错误的样例。

#include <bits/stdc++.h>
using namespace std;
const int maxn = (1e5 + 5), Y = 1e9;
struct Node {
	int id, x, y;
	int prepos; //前一点
	int nxtpos; //后一点
	int startx, endpos;   // 最终位置
	int bropos;
}e[maxn * 4];
struct cmp {
	int pos, y;
	cmp(int p, int yy) : pos(p), y(yy) {}
	bool operator<(const cmp &b) const {
		return y < b.y;
	}
};
vector<cmp> v[maxn];
int cnt = 1, f[maxn];
void Init(int n) {
	for(int i = 1; i <= n; ++i) {
		f[i] = i;
		e[cnt++] = {cnt - 1, i, 0, -1, cnt, i, i, cnt - 1};
		e[cnt++] = {cnt - 1, i, Y, cnt - 2, -1, i, i, cnt - 1};
		v[i].push_back({cnt - 2, 0});
		v[i].push_back({cnt - 1, Y});
	}
}
void count(int n) {
	long long sum = 0;
	for(int i = 1; i <= n; ++i) {
//		f[i] = dfs(2 * i - 1, i);
		sum += i * f[i];
	}
	printf("%lld\n", sum);
}
bool cmp1(cmp a, cmp b) {
	return a.y < b.y;
}
int main() {
	int n, q;
	cin >> n >> q;
	Init(n);
	while(q--) {
		char op;
		int x1, x2, y;
		scanf(" %c%d%d%d", &op, &x1, &x2, &y);
		
		auto vpos1 = lower_bound(v[x1].begin(), v[x1].end(), cmp(-1, y));
		int pos1 = vpos1 - v[x1].begin();
		int pre1 = v[x1][pos1 - 1].pos, nxt1 = v[x1][pos1].pos;
		auto vpos2 = lower_bound(v[x2].begin(), v[x2].end(), cmp(-1, y));
		int pos2 = vpos2 - v[x2].begin();
		int pre2 = v[x2][pos2 - 1].pos, nxt2 = v[x2][pos2].pos;
			
		if(op == '+') {
			e[cnt++] = {cnt - 1, x1, y, pre2, nxt1, e[pre2].startx, e[nxt1].endpos, cnt};
			e[cnt++] = {cnt - 1, x2, y, pre1, nxt2, e[pre1].startx, e[nxt2].endpos, cnt - 2};    // x,y,前一点,后一点,最终
			e[pre1].nxtpos = cnt - 1; e[pre1].endpos = e[cnt - 1].endpos;
			e[nxt1].prepos = cnt - 2; e[nxt1].startx = e[cnt - 2].startx;
			e[pre2].nxtpos = cnt - 2; e[pre2].endpos = e[cnt - 2].endpos;
			e[nxt1].prepos = cnt - 1; e[nxt2].startx = e[cnt - 1].startx;
			v[x1].insert(vpos1, cmp(cnt - 2, y));
			v[x2].insert(vpos2, cmp(cnt - 1, y));
			f[e[cnt - 2].startx] = e[cnt - 2].endpos;
			f[e[cnt - 1].startx] = e[cnt - 1].endpos;
		}
		else {
			nxt1 = v[x1][pos1 + 1].pos; nxt2 = v[x2][pos2 + 1].pos;
			e[pre1].nxtpos = e[nxt1].bropos; e[nxt1].prepos = e[pre1].bropos;
			e[nxt1].startx = e[e[pre1].bropos].startx;
			e[pre2].nxtpos = e[nxt2].bropos; e[nxt2].prepos = e[pre2].bropos;
			e[nxt2].startx = e[e[pre2].bropos].startx;
			f[e[v[x1][pos1].pos].startx] = e[e[nxt2].bropos].endpos;
			f[e[v[x2][pos2].pos].startx] = e[e[nxt1].bropos].endpos;
			v[x1].erase(vpos1);
			v[x2].erase(vpos2);
		}
//		for(int i = 1; i <= n; ++i) cout << f[i] << " ";
//		cout << endl;
		count(n);
	}
	return 0;
}

L3-3 可怜的复杂度

一看出题人 北京大学 吉如一 秒怂,巨佬出的题肯定不会。

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值