2018嘉庚编程大赛题解+感悟

2018年的编程大赛就这样过去了,真的是一点优势没有,bfs,dijkstra和并查集我刷过的题目赵大佬都刷过,而且考了高数的,他们在学肯定印象深刻,我一年没动了,打不过打不过,现在只能万年老二了,最对不起的就是许振明和寒假的并查集了,水题还翻了3次,起手送了1h+2题

比赛的时候vs不能复制案例,freopen不安全,freopen_s不会用,忘记怎么去掉安全了,所以有的题目我案例都没打直接交了

解决方案:先运行一次printf/scanf/freopen,在报错信息里找到 _CRT_SECURE_NO_DEPRECATE,复制


右键项目名(Project 那个)属性,


点c/c++->预处理器->预处理器定义右边那个,然后有个编辑,点进去,粘贴刚刚复制的,就可以用freopen了

freopen有三个参数,这里只讲怎么读文件,第一个参数是文件名,比如"test.txt",注意相对路径就要和cpp文件放一起,不然就写绝对路径,

第二个参数“r“,代表read

第三个参数stdin

然后把案例复制到test.txt里,就解决了复制不了的问题了

题目链接

题解:

热身赛:1神奇的衣架2

lru算法,开数组,一个来存衣服的新旧程度(相当于队列),一个存在队列的位置,一个存在衣架的位置,一个存衣服在衣架的哪

如果是已经在衣架上了,就找一下衣服在队列的哪里,然后删掉,入队

如果衣架满了,就把队列第一个删了,再把新的入队

否则直接入队

快还是曾大佬快,我的是52ms,曾大佬16ms

#include<stdio.h>
#include<cstring>
int main() {
	const int maxN = 10001;
	//out是在衣架的位置,hanger是衣架,pos是在队列的位置,queue是队列
	int out[maxN], hanger[maxN], pos[maxN], queue[maxN], n, a, b, c, t, head, rear, preClothing, prePos;
	scanf("%d", &n);
	while (n--) {
		scanf("%d%d%d", &a, &b, &c);
		head = rear = 0;
		memset(out, -1, sizeof(out));
		memset(hanger, 0, sizeof(hanger));
		memset(pos, -1, sizeof(pos));
		while (c--) {
			scanf("%d", &t);
			if (out[t] >= 0) {
				queue[rear] = t;
				for (int i = pos[t]; i < rear; ++i) {
					queue[i] = queue[i + 1];
					pos[queue[i]] = i;
				}
			}
			else if (rear - head >= a) {
				preClothing = queue[head];
				prePos = out[preClothing];
				pos[preClothing] = -1;
				out[preClothing] = -1;
				pos[t] = rear;
				out[t] = prePos;
				queue[rear] = t;
				hanger[prePos] = t;
				++head;
				++rear;
			}
			else {
				out[t] = rear;
				pos[t] = rear;
				hanger[rear] = t;
				queue[rear] = t;
				++rear;
			}
		}
		for (int i = 0; i<a; ++i) {
			printf("%d", hanger[i]);
			if (i == a - 1)printf("\n");
			else printf(" ");
		}
	}
	return 0;
}

2合法的ip

这题。。。打的有心理阴影了,因为当时是一个3分钟2000b的人出的题目,而且有个00.0.0.0他没点明,我,大柱,曾大佬,柚子好像都崩了,第二天才改的,然后蔡老改到了oj,这题其实是个水题

#include<iostream>
#include<string>
using namespace std;
bool check(const string &s) {
	if (s.size() == 1 && "0" <= s && s <= "9" || s.size() == 2 && "10" <= s && s <= "99" || s.size() == 3 && "100" <= s && s <= "255")return true;
	return false;
}
bool isLeagal(const string &s) {
	int pre = -1, cnt = 0;
	for (int i = 0; i<s.size(); ++i) {
		if (s[i] >= '0'&&s[i] <= '9')continue;
		if (s[i] == '.') {
			++cnt;
			if (cnt>3 || !check(s.substr(pre + 1, i - pre - 1)))return false;
			pre = i;
		}
		else return false;
	}
	if (cnt != 3 || !check(s.substr(pre + 1, s.size() - pre - 1)))return false;
	return true;
}
int main() {
	int n;
	string s;
	scanf("%d", &n);
	while (n--) {
		cin >> s;
		printf("%s\n", isLeagal(s) ? "legal" : "illegal");
	}
	return 0;
}

3.凸多边形?凹多边形?

用叉积,沿着边缘走一圈,每次顺序地选三个点 i<=j<=k然后i->j组成一个向量,i->k组成一个,叉积<0就是凹的

#include<iostream>
const int maxN = 100;
int x[maxN], y[maxN];
bool multiplicationCross(const int &a, const int &b, const int &c) {
	return (x[b] - x[a])*(c[y] - a[y]) - (y[b] - y[a])*(x[c] - x[a]) >= 0;
}
int main() {
	bool flag;
	int n, T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 0; i < n; ++i)scanf("%d%d", &x[i], &y[i]);
		flag = true;
		for (int i = 0; i < n; ++i) {
			if (!multiplicationCross(i, (i + 1) % n, (i + 2) % n)) {
				flag = false;
				break;
			}
		}
		puts(flag ? "convex" : "concave");
	}
	return 0;
}

正式赛

4.程序员的段子

作为比赛的第一题本来应该是送分的,我起手翻车3次,送了1h,其实直接慢慢乘上去,>=就停。

#include<iostream>
#include<cmath>
int main() {
	int n, a, t;
	scanf("%d", &n);
	while (n--) {
		scanf("%d", &a);
		t = pow(2, (int)log2(a));
		printf("%d\n", t >= a ? t : t * 2);
	}
	return 0;
}

5黄金周

其实就是排序+贪心

比赛的时候我第一反应dp,看到他们都A了,我就想肯定是贪心,严格证明了一下确实是贪心,然后就A了

#include<iostream>
#include<algorithm>
int main() {
	int a[1000], n, m, p;
	scanf("%d", &n);
	while (n--) {
		scanf("%d%d", &m, &p);
		for (int i = 0; i < p; ++i)scanf("%d", &a[i]);
		std::sort(a, a + p);
		int pre = 0, cnt = 1;
		for (int i = 1; i < p; ++i) {
			if (pre + a[i] + 1 > m)break;
			pre += a[i] + 1;
			++cnt;
		}
		printf("%d\n", cnt);
	}
	return 0;
}

6.受伤的触摸屏

当时比赛死活想不起来,毕竟一年没用了,对不起许振明

4点共面,混合积=0或者线性相关的行列式=0

#include<iostream>
int main() {
	int n, x[4], y[4], z[4], r[3][3];
	scanf("%d", &n);
	while (n--) {
		scanf("%d%d%d", &x[0], &y[0], &z[0]);
		for (int i = 1; i <= 3; ++i) {
			scanf("%d%d%d", &x[i], &y[i], &z[i]);
			r[i - 1][0] = x[i] - x[0];
			r[i - 1][1] = y[i] - y[0];
			r[i - 1][2] = z[i] - z[0];
		}
		if (r[0][0] * r[1][1] * r[2][2] + r[0][1] * r[1][2] * r[2][0] + r[1][0] * r[2][1] * r[0][2] - r[0][2] * r[1][1] * r[2][0] - r[0][1] * r[1][0] * r[2][2] - r[0][0] * r[1][2] * r[2][1] == 0)
			printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

7.15周年庆代表

前缀和,sum[i]=a[0]+a[1]+..+a[i]

b个就是sum[i]-sum[i-b]

然后遍历一下取最大的

也可以先加b个,然后后移一下,删第一个,加移动到的那个

#include<iostream>
using namespace std;
int main() {
	int n, a, b, score[100001], sum, temp;
	scanf("%d", &n);
	while (n--) {
		scanf("%d%d", &a, &b);
		score[0] = sum = 0;
		for (int i = 1; i <= a; ++i) {
			scanf("%d", &score[i]);
			score[i] += score[i - 1];
		}
		for (int i = b; i <= a; ++i) {
			temp = score[i] - score[i - b];
			if (temp > sum)sum = temp;
		}
		printf("%d\n", sum);
	}
	return 0;
}

8.九宫格输入法

先把前后的#砍了

然后每个字母对应的数字转化出来,相同输出1,

不同把1砍了再比一次

#include<iostream>
#include<string>
using namespace std;
const string c[] = { "2","22","222","3","33","333","4","44","444","5","55","555","6","66","666","7","77","777","7777","8","88","888","9","99","999","9999" };
string change(string &s) {
	string temp;
	for (int i = 0; i < s.length(); ++i) {
		if (s[i] >= 'a'&&s[i] <= 'z')s[i] = s[i] - 'a' + 'A';
		if (s[i] == '#'&&temp[temp.length() - 1] == '1')continue;
		if (s[i] == '#')temp += '1';
		else temp += c[s[i] - 'A'];
	}
	return temp;
}
string change2(const string &s) {
	string temp;
	for (int i = 0; i < s.length(); ++i)
		if (s[i] != '1')temp += s[i];
	return temp;
}
int main() {
	int n;
	string a, b, c, d;
	cin >> n;
	while (n--) {
		cin >> a >> b;
		a = a.substr(a.find_first_not_of("#"));
		a = a.substr(0, a.find_last_not_of("#") + 1);
		b = b.substr(b.find_first_not_of("#"));
		b = b.substr(0, b.find_last_not_of("#") + 1);
		c = change(a);
		d = change(b);
		if (c == d)printf("1\n");
		else printf("%d\n", change2(c) == change2(d) ? 2 : 3);
	}
	return 0;
}

9.异星崛起

gcd两个进化时间,然后按公共进化时间加,知道第一个大于第二个

#include<iostream>
int gcd(int a, int b) {
	int c = a % b;
	while (c) {
		a = b;
		b = c;
		c = a % b;
	}
	return b;
}
int main() {
	int n, c1, d1, e1, c2, d2, e2, f2, day, cnt, base;
	scanf("%d", &n);
	while (n--) {
		scanf("%d%d%d%d%d%d%d", &c1, &d1, &e1, &c2, &d2, &e2, &f2);
		base = gcd(d1, d2);
		day = 0;
		while (c1 <= c2) {
			day += base;
			if (day%d1 == 0)c1 += e1;
			if (day%d2 == 0) {
				c2 += e2;
				if (c2 > f2)c2 = f2;
			}
		}
		printf("%d\n", day);
	}
	return 0;
}

10.廉价航空

注意一下用map存新来的星球之后就是裸的dijkstra(不懂的点这里dijkstra),但是比赛的时候忘记了。。。现推的= =

下面会给出两种写法,一种是pair的,一种是类的

注意如果pair用的不习惯的话,可以用下面的类的版本,可能比较好理解

#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
using namespace std;
const int N = 101, inf = 0x3f3f3f3f;
int dis[N];
vector<pair<int, int> > edge[N];
void dijkstra() {
	dis[1] = 0;
	priority_queue<pair<int, int> > q;
	q.push(make_pair(0, 1));
	while (!q.empty()) {
		int now = q.top().second;
		q.pop();
		if (now == 2) {
			printf("%d\n", dis[2]);
			return;
		}
		for (int i = 0; i < edge[now].size(); ++i) {
			int t = edge[now][i].first;
			if (edge[now][i].second + dis[now] < dis[t]) {
				dis[t] = edge[now][i].second + dis[now];
				q.push(make_pair(-dis[t], t));
			}
		}
	}
	printf("-1\n");
}
int main() {
	string a, b;
	int n, m, p, k;
	map<string, int> name;
	cin >> n;
	while (n--) {
		cin >> m >> a >> b;
		memset(dis, inf, sizeof(dis));
		for (int i = 0; i < N; ++i)edge[i].clear();
		name.clear();
		name[a] = 1;
		name[b] = 2;
		k = 2;
		while (m--) {
			cin >> a >> b >> p;
			if (!name[a]) {
				++k;
				name[a] = k;
			}
			if (!name[b]) {
				++k;
				name[b] = k;
			}
			edge[name[a]].push_back(make_pair(name[b], p));
		}
		dijkstra();
	}
	return 0;
}

类的版本

#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
using namespace std;
class point {
public:
	int v, cost;
	point(const int &v=0, const int &cost=0) :v(v), cost(cost) {}
	//注意优先队列从大到小排,而且用结构体和类要重载小于号,所以小于号要反着重载
	bool operator <(const point &t)const {
		if (cost != t.cost)return cost > t.cost;
		return v > t.v;
	}
};
const int N = 405, inf = 0x3f3f3f3f;
vector<point> edge[N];
int dis[N];
void dijkstra() {
	memset(dis, inf, sizeof(dis));
	dis[1] = 0;
	priority_queue<point> q;
	q.push(point(1, 0));
	while (!q.empty()) {
		int now = q.top().v;
		if (now == 2) {
			printf("%d\n", dis[2]);
			return;
		}
		q.pop();
		for (int i = 0; i < edge[now].size(); ++i) {
			int v = edge[now][i].v;
			if (dis[now] + edge[now][i].cost < dis[v]) {
				dis[v] = dis[now] + edge[now][i].cost;
				q.push(point(v, dis[v]));
			}
		}
	}
	printf("-1\n");
}
int main() {
	map<string, int> name;
	int n, T, cnt, cost;
	string a, b;
	scanf("%d", &T);
	while (T--) {
		cin >> n >> a >> b;
		name.clear();
		for (int i = 0; i < N; ++i)edge[i].clear();
		name[a] = 1;
		name[b] = 2;
		cnt = 2;
		while (n--) {
			cin >> a >> b >> cost;
			if (!name[a]) {
				++cnt;
				name[a] = cnt;
			}
			if (!name[b]) {
				++cnt;
				name[b] = cnt;
			}
			edge[name[a]].push_back(point(name[b], cost));
		}
		dijkstra();
	}
	return 0;
}

11.星际旅行

也是用map存新来的星球,然后裸的并查集(不会的点这 并查集),真的对不起寒假刷的并查集

#include<iostream>
#include<string>
#include<map>
using namespace std;
const int N = 2005;
int pre[N], r[N];
int find(const int &x) {
	int j, k = x, r = x;
	while (pre[r] != r)r = pre[r];
	while (k != r) {
		j = pre[k];
		pre[k] = r;
		k = j;
	}
	return r;
}
void unite(int x, int y) {
	x = find(x), y = find(y);
	if (x == y)return;
	if (r[x] < r[y])pre[x] = y;
	else {
		pre[y] = x;
		if (r[x] == r[y])++r[x];
	}
}
int main() {
	map<string, int> p;
	int n, m, k;
	string a, b;
	scanf("%d", &n);
	while (n--) {
		scanf("%d", &m);
		cin >> a >> b;
		p.clear();
		for (int i = 1; i < N; ++i) {
			pre[i] = i;
			r[i] = 1;
		}
		p[a] = 1;
		p[b] = 2;
		k = 2;
		while (m--) {
			cin >> a >> b;
			if (!p[a]) {
				++k;
				p[a] = k;
			}
			if (!p[b]) {
				++k;
				p[b] = k;
			}
			unite(p[a], p[b]);
		}
		printf("%s\n", find(1) == find(2) ? "YES" : "NO");
	}
	return 0;
}

12.日期博弈

这题比赛的时候没敢想,但是仔细一想只是基础的博弈论

N局面先手必胜,P局面先手必败

无法移动的是P局面,能移动到P的是N局面,所有子局面是N局面的是P局面

2018.3.31是P局面,然后用dp往回推一下就好了

#include<iostream>
#include<cstring>
bool isLeap(const int &y) {
	return y % 400 == 0 || y % 4 == 0 && y % 100 != 0;
}
int main() {
	const int day[2][13] =
	{ { 0,31,28,31,30,31,30,31,31,30,31,30,31 },
	{ 0,31,29,31,30,31,30,31,31,30,31,30,31 } };
	//0-118  +1900=1900-2018
	bool win[120][13][32];
	bool nextDay;
	int n, y = 118, m = 3, d = 31, ty, tm;
	memset(win, true, sizeof(win));
	//无法移动的是P局面
	win[y][m][d] = false;
	do {
		//缓存明天的状态
		nextDay = win[y][m][d];
		//今天的,也就是往前一天
		--d;
		if (d <= 0) {
			--m;
			if (m <= 0) {
				--y;
				m = 12;
			}
			d = day[isLeap(y + 1900)][m];
		}
		//下个月的
		ty = y;
		tm = m + 1;
		if (tm == 13) {
			tm = 1;
			++ty;
		}
		//判断子局面是不是都是N局面
		if (nextDay && win[ty][tm][d] && win[y + 1][m][d])win[y][m][d] = false;
	} while (y || m != 1 || d != 1);
	scanf("%d", &n);
	while (n--) {
		scanf("%d%d%d", &y, &m, &d);
		printf("%s\n", win[y - 1900][m][d] ? "YES" : "NO");
	}
	return 0;
}

13.加减法

。。。模拟,大部分的数字是可以通过4个位置来确定的

#include<iostream>
#include<cstring>
int main() {
	char map[5][10000];
	memset(map, ' ', sizeof(map));
	int len = 0, t;
	for (int i = 0; i < 5; ++i) {
		std::cin.getline(map[i], 9999);
		t = strlen(map[i]);
		if (t > len)len = t;
	}
	++len;
	bool plus = true;
	int pre = 0, now;
	for (int i = 0; i < len;) {
		if (map[0][i] == ' '&&map[1][i] == ' '&&map[2][i] == ' '&&map[3][i] == ' '&&map[4][i] == ' ')++i;
		else if (map[2][i] == '*'&&map[2][i + 1] == '*'&&map[2][i + 2] == '*'&&map[2][i + 3] == ' ') {
			plus = map[3][i + 1] == '*';
			i += 3;
		}
		else if (map[0][i] == '*'&&map[1][i] == '*'&&map[2][i] == '*'&&map[3][i] == '*'&&map[4][i] == '*'&&map[0][i + 1] == ' ') {
			now = 1;
			++i;
			if (plus)pre += now;
			else pre -= now;
		}
		else {
			char x = map[1][i], y = map[3][i], z = map[1][i + 4], r = map[3][i + 4];
			if (x == '*'&&y == '*'&&z == '*'&&r == '*')now = map[2][i + 1] == '*' ? 8 : 0;
			else if (x == ' '&&y == '*'&&z == '*'&&r == ' ')now = 2;
			else if (x == ' '&&y == ' '&&z == '*'&&r == '*')now = map[2][i] == '*' ? 3 : 7;
			else if (x == '*'&&y == ' '&&z == ' '&&r == ' ')now = 4;
			else if (x == '*'&&y == ' '&&z == ' '&&r == '*')now = 5;
			else if (x == '*'&&y == '*'&&z == ' '&&r == '*')now = 6;
			else if (x == '*'&&y == ' '&&z == '*'&&r == '*')now = 9;
			else {
				++i;
				continue;
			}
			i += 5;
			if (plus)pre += now;
			else pre -= now;
		}
	}
	printf("%d", pre);
	return 0;
}

给你们几个案例

*****      *****     *****

*   *   *  *   *         *
*****  *** ***** *** *****
    *   *  *   *     *    
*****      *****     *****






*****      *****     *
*   *   *  *   *     *
*****  *** ***** *** *
    *   *  *   *     *
*****      *****     *


*****      *****     *
    *   *  *   *     *
*****  *** ***** *** *
    *   *  *   *     *
*****      *****     *


*  *       *****     *
*  *    *  *   *     *
*****  *** ***** *** *
   *    *  *   *     *
   *       *****     *


*****      *****     *
*       *  *   *     *
*****  *** ***** *** *
*   *   *  *   *     *
*****      *****     *


*****      *****     *
    *   *  *   *     *
    *  *** ***** *** *
    *   *  *   *     *
    *      *****     *


*****      *****     *
*   *   *  *   *     *
*****  *** ***** *** *
*   *   *  *   *     *
*****      *****     *




*****      *****     *
*   *   *  *   *     *
*****  *** ***** *** *
    *   *  *   *     *
*****      *****     *




    *****     *****
    *   *  *  *   *
*** ***** *** *****
        *  *  *   *
    *****     *****




*****      *****     *
*       *  *   *     *
*****  *** ***** *** *
    *   *  *   *     *
*****      *****     *

比赛后加的题目14.三角形

先判断是否共线,然后算空间直线的角度

#include<iostream>
int main() {
	int x[3], y[3], z[3], n, vec[3][4];
	scanf("%d", &n);
	while (n--) {
		for (int i = 0; i < 3; ++i)scanf("%d%d%d", &x[i], &y[i], &z[i]);
		vec[0][0] = x[1] - x[0]; vec[0][1] = y[1] - y[0]; vec[0][2] = z[1] - z[0];
		vec[1][0] = x[2] - x[0]; vec[1][1] = y[2] - y[0]; vec[1][2] = z[2] - z[0];
		vec[2][0] = x[2] - x[1]; vec[2][1] = y[2] - y[1]; vec[2][2] = z[2] - z[1];
		vec[0][3] = vec[1][0] * vec[0][0] + vec[1][1] * vec[0][1] + vec[1][2] * vec[0][2];
		vec[1][3] = -vec[2][0] * vec[0][0] - vec[2][1] * vec[0][1] - vec[2][2] * vec[0][2];
		vec[2][3] = vec[2][0] * vec[1][0] + vec[2][1] * vec[1][1] + vec[2][2] * vec[1][2];
		if (vec[1][1] * vec[0][0] == vec[1][0] * vec[0][1] && vec[0][1] * vec[1][2] == vec[1][1] * vec[0][2] && vec[0][0] * vec[1][2] == vec[1][0] * vec[0][2])printf("4\n");
		else if (vec[0][3] < 0 || vec[1][3] < 0 || vec[2][3] < 0)printf("3\n");
		else if (!vec[0][3] || !vec[1][3] || !vec[2][3])printf("1\n");
		else printf("2\n");
	}
	return 0;
}



猜你喜欢

转载自blog.csdn.net/qq_39942341/article/details/79810667