中国石油大学天梯赛真题模拟第一场
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。
输入格式:
输入第一行给出3个正整数:N
(≤100),即前来参宴的宾客总人数,则这些人从1到N
编号;M
为已知两两宾客之间的关系数;K
为查询的条数。随后M
行,每行给出一对宾客之间的关系,格式为:宾客1 宾客2 关系
,其中关系
为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K
行,每行给出一对需要查询的宾客编号。
这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。
输出格式:
对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出No problem
;如果他们之间并不是朋友,但也不敌对,则输出OK
;如果他们之间有敌对,然而也有共同的朋友,则输出OK but...
;如果他们之间只有敌对关系,则输出No way
。
输入样例:
7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2
输出样例:
No problem OK OK but... No way
很迷的题面,令人头秃。训练的时候有一个点没过(因为当时开始写了-1的最短路,最外层循环只for到2……我也不知道当时怎么想的)
#include "bits/stdc++.h" using namespace std; const int maxn = 200; int mp[maxn][maxn], fri[maxn][maxn], ene[maxn][maxn]; int main() { int n, m, k; cin >> n >> m >> k; int a, b, c; for (int i = 1; i <= m; i++) { cin >> a >> b >> c; mp[a][b] = c; mp[b][a] = c; if (c == 1) { fri[a][b] = c; fri[b][a] = c; } else { ene[a][b] = c; ene[b][a] = b; } } for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (fri[i][k] == 1 && fri[k][j] == 1) { fri[i][j] = 1; } } } } while (k--) { cin >> a >> b; if (mp[a][b] == -1 && fri[a][b] == 0) { cout << "No way" << endl; } else if (mp[a][b] == -1 && fri[a][b] == 1) { cout << "OK but..." << endl; } else if (mp[a][b] == 0) { cout << "OK" << endl; } else cout << "No problem" << endl; } return 0; }
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数N
(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
输入样例:
7
1 2 3 4 5 6 7
4 1 3 2 6 5 7
输出样例:
4 6 1 7 5 3 2
用递归可以从一个树得到其遍历序列,那么从遍历序列得到树将其递归反过来就行。反转操作可以忽略,在bfs的时候先插入右儿子就可以了
#include "bits/stdc++.h" using namespace std; const int maxn = 100; int dlr[maxn], ldr[maxn]; int n; typedef struct node { int val; node *l, *r; } *tree; tree root = NULL; void build(tree &t, int start, int lasttime, int len) { if (len <= 0) { t = NULL; return; } int now; t = (tree) malloc(sizeof(node)); t->val = dlr[start]; for (int i = 0; i < n; i++) { if (ldr[i] == dlr[start]) { now = i; break; } } int lenl = now - lasttime; build(t->l, start + 1, lasttime, lenl); int lenr = len - 1 - lenl; build(t->r, start + lenl + 1, now + 1, lenr); } void bfs() { queue<tree> q; q.push(root); int flag = 0; tree temp; while (!q.empty()) { if (flag) printf(" "); flag = 1; temp = q.front(); q.pop(); cout << temp->val; if (temp->r != NULL) q.push(temp->r); if (temp->l != NULL) q.push(temp->l); } } int main() { cin >> n; for (int i = 0; i < n; i++) { cin >> ldr[i]; } for (int i = 0; i < n; i++) { cin >> dlr[i]; } build(root, 0, 0, n); bfs(); return 0; }
将一系列给定数字顺序插入一个初始为空的小顶堆H[]
。随后判断一系列相关命题是否为真。命题分下列几种:
x is the root
:x
是根结点;x and y are siblings
:x
和y
是兄弟结点;x is the parent of y
:x
是y
的父结点;x is a child of y
:x
是y
的一个子结点。
输入格式:
每组测试第1行包含2个正整数N
(≤ 1000)和M
(≤ 20),分别是插入元素的个数、以及需要判断的命题数。下一行给出区间[−10000,10000]内的N
个要被插入一个初始为空的小顶堆的整数。之后M
行,每行给出一个命题。题目保证命题中的结点键值都是存在的。
输出格式:
对输入的每个命题,如果其为真,则在一行中输出T
,否则输出F
。
输入样例:
5 4
46 23 26 24 10
24 is the root
26 and 23 are siblings
46 is the parent of 23
23 is a child of 10
输出样例:
F
T
F
T
判断两个节点是兄弟只要判断他们的父亲是同一个节点就可以了,比赛的时候写了判断他们相邻且一个在奇数节点一个在偶数节点。。。。
// make_heap(heap + 1, heap + 1 + i, greater<int>());
//c++自带的建树函数,默认建大根堆
#include "bits/stdc++.h" using namespace std; const int maxn = 1e5; const int base = 11000; int heap[maxn]; int ver[maxn]; int n, m; void up(int p) { while (p > 1) { if (heap[p] < heap[p / 2]) { swap(heap[p], heap[p / 2]); p /= 2; } else break; } } int main() { cin >> n >> m; int x; for (int i = 1; i <= n; i++) { cin >> x; heap[i] = x; up(i); // make_heap(heap + 1, heap + 1 + i, greater<int>()); //c++自带的建树函数,默认建大根堆 } for (int i = 1; i <= n; i++) { ver[heap[i] + base] = i;//去掉负数 } char str[10000]; int a, b; while (m--) { scanf("%d", &a); scanf("%s", str); if (str[0] == 'a') { scanf("%d", &b); scanf("%s", str); scanf("%s", str); if (ver[a + base] / 2 == ver[b + base] / 2) {//a和b是兄弟 cout << "T" << endl; } else cout << "F" << endl; } else { scanf("%s", str); if (str[0] == 'a') { scanf("%s", str); scanf("%s", str); scanf("%d", &b); if (ver[a + base] / 2 == ver[b + base])//a是b的儿子 cout << "T" << endl; else cout << "F" << endl; } else { scanf("%s", str); if (str[0] == 'r') {//a是树根 if (heap[1] == a) cout << "T" << endl; else cout << "F" << endl; } else { scanf("%s", str); scanf("%d", &b); if (ver[a + base] == ver[b + base] / 2)//a是b的父亲 cout << "T" << endl; else cout << "F" << endl; } } } } return 0; }
本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。
输入格式:
输入在第一行给出两个正整数N
(2 ≤ N
≤ 500)和M
,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M
行,每行按如下格式给出一条道路的信息:
V1 V2 one-way length time
其中V1
和V2
是道路的两个端点的编号(从0到N
-1);如果该道路是从V1
到V2
的单行线,则one-way
为1,否则为0;length
是道路的长度;time
是通过该路所需要的时间。最后给出一对起点和终点的编号。
输出格式:
首先按下列格式输出最快到达的时间T
和用节点编号表示的路线:
Time = T: 起点 => 节点1 => ... => 终点
然后在下一行按下列格式输出最短距离D
和用节点编号表示的路线:
Distance = D: 起点 => 节点1 => ... => 终点
如果最快到达路线不唯一,则输出几条最快路线中最短的那条,题目保证这条路线是唯一的。而如果最短距离的路线不唯一,则输出途径节点数最少的那条,题目保证这条路线是唯一的。
如果这两条路线是完全一样的,则按下列格式输出:
Time = T; Distance = D: 起点 => 节点1 => ... => 终点
输入样例1:
10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
5 4 0 2 3
5 9 1 1 4
0 6 0 1 1
7 3 1 1 2
8 3 1 1 2
2 5 0 2 2
2 1 1 1 1
1 5 0 1 3
1 4 0 1 1
9 7 1 1 3
3 1 0 2 5
6 3 1 2 1
5 3
输出样例1:
Time = 6: 5 => 4 => 8 => 3
Distance = 3: 5 => 1 => 3
输入样例2:
7 9
0 4 1 1 1
1 6 1 3 1
2 6 1 1 1
2 5 1 2 2
3 0 0 1 1
3 1 1 3 1
3 2 1 2 1
4 5 0 2 2
6 5 1 2 1
3 5
输出样例2:
Time = 3; Distance = 4: 3 => 2 => 5
读题读题读题,比赛时读题要尽量的慢,准,稳。
#include "bits/stdc++.h" using namespace std; const int maxn = 600; int timemap[maxn][maxn], lenmap[maxn][maxn]; int lendist[maxn], lt[maxn], tl[maxn], timedist[maxn], timepre[maxn], timevis[maxn]; int lenpre[maxn], lenvis[maxn]; int n, m, s, d; vector<int> ans1, ans2; bool check() { if (ans1.size() != ans2.size()) return false; for (int i = 0; i < ans1.size(); i++) { if (ans1[i] != ans2[i]) return false; } return true; } void dij1() { memset(timedist, 0x3f, sizeof(timedist)); memset(timevis, 0, sizeof(timevis)); memset(timepre, -1, sizeof(timepre)); memset(lendist, 0x3f, sizeof(lendist)); memset(lenvis, 0, sizeof(lenvis)); memset(lenpre, -1, sizeof(lenpre)); memset(lt, 0x3f, sizeof(lt)); memset(tl, 0x3f, sizeof(lt)); timedist[s] = 0; lendist[s] = 0; lt[s] = 1; tl[s] = 0; for (int i = 1; i < n; i++) { int x = -1; for (int j = 0; j < n; j++) { if (!timevis[j] && (x == -1 || timedist[j] < timedist[x])) x = j; } timevis[x] = 1; for (int j = 0; j < n; j++) { if (timedist[x] + timemap[x][j] < timedist[j]) { timedist[j] = timedist[x] + timemap[x][j]; tl[j] = tl[x] + lenmap[x][j]; timepre[j] = x; } else if (timedist[x] + timemap[x][j] == timedist[j] && tl[x] + lenmap[x][j] < tl[j]) { tl[j] = tl[x] + lenmap[x][j]; timepre[j] = x; } } x = -1; for (int j = 0; j < n; j++) { if (!lenvis[j] && (x == -1 || lendist[j] < lendist[x])) x = j; } lenvis[x] = 1; for (int j = 0; j < n; j++) { if (lendist[x] + lenmap[x][j] < lendist[j]) { lendist[j] = lendist[x] + lenmap[x][j]; lenpre[j] = x; lt[j] = lt[x] + 1; } else if (lendist[x] + lenmap[x][j] == lendist[j] && lt[x] + 1 < lt[j]) { lenpre[j] = x; lt[j] = lt[x] + 1; } } } ans1.push_back(d); int x = timepre[d]; while (x != -1) { ans1.push_back(x); x = timepre[x]; } ans2.push_back(d); x = lenpre[d]; while (x != -1) { ans2.push_back(x); x = lenpre[x]; } if (check()) { printf("Time = %d; Distance = %d: ", timedist[d], lendist[d]); int flag = 0; for (int i = ans1.size() - 1; i >= 0; i--) { if (flag) printf(" => "); flag = 1; printf("%d", ans1[i]); } printf("\n"); return; } printf("Time = %d: ", timedist[d]); int flag = 0; for (int i = ans1.size() - 1; i >= 0; i--) { if (flag) printf(" => "); flag = 1; printf("%d", ans1[i]); } printf("\n"); printf("Distance = %d: ", lendist[d]); flag = 0; for (int i = ans2.size() - 1; i >= 0; i--) { if (flag) printf(" => "); flag = 1; printf("%d", ans2[i]); } printf("\n"); } int main() { //freopen("input.txt", "r", stdin); memset(lenmap, 0x3f, sizeof(lenmap)); memset(timemap, 0x3f, sizeof(timemap)); cin >> n >> m; int a, b, c, dd, e; for (int i = 0; i < m; i++) { cin >> a >> b >> c >> dd >> e; if (!c) { timemap[b][a] = e; lenmap[b][a] = dd; } timemap[a][b] = e; lenmap[a][b] = dd; } cin >> s >> d; dij1(); return 0; }
喊山 (30 分)
喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂—喂喂喂……”的呼唤。呼唤声通过空气的传递,回荡于深谷之间,传送到人们耳中,发出约定俗成的“讯号”,达到声讯传递交流的目的。原来它是彝族先民用来求援呼救的“讯号”,慢慢地人们在生活实践中发现了它的实用价值,便把它作为一种交流工具世代传袭使用。(图文摘自:http://news.xrxxw.com/newsshow-8018.html)
一个山头呼喊的声音可以被临近的山头同时听到。题目假设每个山头最多有两个能听到它的临近山头。给定任意一个发出原始信号的山头,本题请你找出这个信号最远能传达到的地方。
输入格式:
输入第一行给出3个正整数n
、m
和k
,其中n
(≤10000)是总的山头数(于是假设每个山头从1到n
编号)。接下来的m
行,每行给出2个不超过n
的正整数,数字间用空格分开,分别代表可以听到彼此的两个山头的编号。这里保证每一对山头只被输入一次,不会有重复的关系输入。最后一行给出k
(≤10)个不超过n
的正整数,数字间用空格分开,代表需要查询的山头的编号。
输出格式:
依次对于输入中的每个被查询的山头,在一行中输出其发出的呼喊能够连锁传达到的最远的那个山头。注意:被输出的首先必须是被查询的个山头能连锁传到的。若这样的山头不只一个,则输出编号最小的那个。若此山头的呼喊无法传到任何其他山头,则输出0。
输入样例:
7 5 4
1 2
2 3
3 1
4 5
5 6
1 4 5 7
输出样例:
2
6
4
0
简单bfs
#include "bits/stdc++.h" using namespace std; const int maxn = 10010; vector<int> e[maxn]; struct node { int pos, dis; }; int n, m, k; int vis[maxn]; int ans, dis; void bfs(int x) { queue<node> q; node temp, tt; temp.dis = 0; temp.pos = x; vis[x] = 1; q.push(temp); while (!q.empty()) { temp = q.front(); q.pop(); if (temp.dis > dis) { dis = temp.dis; ans = temp.pos; } else if (temp.dis == dis && temp.pos < ans) { ans = temp.pos; } for (auto p:e[temp.pos]) { if (!vis[p]) { vis[p] = 1; tt.pos = p; tt.dis = temp.dis + 1; q.push(tt); } } } } int main() { cin >> n >> m >> k; int a, b; for (int i = 0; i < m; i++) { cin >> a >> b; e[a].push_back(b); e[b].push_back(a); } while (k--) { cin >> a; ans = 0x3f3f3f3f; dis = -0x3f3f3f3f; bfs(a); cout << (ans == a ? 0 : ans) << endl; memset(vis, 0, sizeof(vis)); } return 0; }
正如我们所知,中国古代长城的建造是为了抵御外敌入侵。在长城上,建造了许多烽火台。每个烽火台都监视着一个特定的地区范围。一旦某个地区有外敌入侵,值守在对应烽火台上的士兵就会将敌情通报给周围的烽火台,并迅速接力地传递到总部。
现在如图1所示,若水平为南北方向、垂直为海拔高度方向,假设长城就是依次相联的一系列线段,而且在此范围内的任一垂直线与这些线段有且仅有唯一的交点。
图 1
进一步地,假设烽火台只能建造在线段的端点处。我们认为烽火台本身是没有高度的,每个烽火台只负责向北方(图1中向左)瞭望,而且一旦有外敌入侵,只要敌人与烽火台之间未被山体遮挡,哨兵就会立即察觉。当然,按照这一军规,对于南侧的敌情各烽火台并不负责任。一旦哨兵发现敌情,他就会立即以狼烟或烽火的形式,向其南方的烽火台传递警报,直到位于最南侧的总部。
以图2中的长城为例,负责守卫的四个烽火台用蓝白圆点示意,最南侧的总部用红色圆点示意。如果红色星形标示的地方出现敌情,将被哨兵们发现并沿红色折线将警报传递到总部。当然,就这个例子而言只需两个烽火台的协作,但其他位置的敌情可能需要更多。
然而反过来,即便这里的4个烽火台全部参与,依然有不能覆盖的(黄色)区域。
图 2
另外,为避免歧义,我们在这里约定,与某个烽火台的视线刚好相切的区域都认为可以被该烽火台所监视。以图3中的长城为例,若A、B、C、D点均共线,且在D点设置一处烽火台,则A、B、C以及线段BC上的任何一点都在该烽火台的监视范围之内。
图 3
好了,倘若你是秦始皇的太尉,为不致出现更多孟姜女式的悲剧,如何在保证长城安全的前提下,使消耗的民力(建造的烽火台)最少呢?
输入格式:
输入在第一行给出一个正整数N
(3 ≤ N
≤105),即刻画长城边缘的折线顶点(含起点和终点)数。随后N
行,每行给出一个顶点的x
和y
坐标,其间以空格分隔。注意顶点从南到北依次给出,第一个顶点为总部所在位置。坐标为区间[−109,109)内的整数,且没有重合点。
输出格式:
在一行中输出所需建造烽火台(不含总部)的最少数目。
输入样例:
10
67 32
48 -49
32 53
22 -44
19 22
11 40
10 -65
-1 -23
-3 31
-7 59
输出样例:
2
求凸包的思想,利用向量叉积判断,如果两个点之间隔着一个上凸(角abc),则从a观测不到c,需在b建一个瞭望台
#include "bits/stdc++.h" using namespace std; const int maxn = 1e5+100; struct point { int x, y; int pos; } st[maxn]; int vis[maxn]; bool check(point a, point b, point c) { return 1ll * (b.x - a.x) * (c.y - b.y) - (c.x - b.x) * (b.y - a.y) <= 0; } int main() { int n; cin >> n; point pos; int s = 0; for (int i = 0; i < n; i++) { cin >> pos.x >> pos.y; pos.pos = i; if (s >= 1) { while (s >= 2 && check(st[s - 2], st[s - 1], pos)) s--; vis[st[s - 1].pos] = 1; } st[s++] = pos; } int ans = 0; for (int i = 1; i <= n; i++) { if (vis[i])ans++; } cout << ans << endl; return 0; }