中国石油大学天梯赛真题模拟第一场

7-10 排座位 (25 分)

布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。

输入格式:

输入第一行给出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;
}
View Code

 


玩转二叉树 (25 分)

给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数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;
}
View Code

关于堆的判断 (25 分)

将一系列给定数字顺序插入一个初始为空的小顶堆H[]。随后判断一系列相关命题是否为真。命题分下列几种:

  • x is the rootx是根结点;
  • x and y are siblingsxy是兄弟结点;
  • x is the parent of yxy的父结点;
  • x is a child of yxy的一个子结点。

输入格式:

每组测试第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;
}
View Code

 


天梯地图 (30 分)

本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。

输入格式:

输入在第一行给出两个正整数N(2 ≤ N ≤ 500)和M,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M行,每行按如下格式给出一条道路的信息:

V1 V2 one-way length time

其中V1V2是道路的两个端点的编号(从0到N-1);如果该道路是从V1V2的单行线,则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个正整数nmk,其中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;
}
View Code

 

长城 (30 分)

正如我们所知,中国古代长城的建造是为了抵御外敌入侵。在长城上,建造了许多烽火台。每个烽火台都监视着一个特定的地区范围。一旦某个地区有外敌入侵,值守在对应烽火台上的士兵就会将敌情通报给周围的烽火台,并迅速接力地传递到总部。

现在如图1所示,若水平为南北方向、垂直为海拔高度方向,假设长城就是依次相联的一系列线段,而且在此范围内的任一垂直线与这些线段有且仅有唯一的交点。

图 1

进一步地,假设烽火台只能建造在线段的端点处。我们认为烽火台本身是没有高度的,每个烽火台只负责向北方(图1中向左)瞭望,而且一旦有外敌入侵,只要敌人与烽火台之间未被山体遮挡,哨兵就会立即察觉。当然,按照这一军规,对于南侧的敌情各烽火台并不负责任。一旦哨兵发现敌情,他就会立即以狼烟或烽火的形式,向其南方的烽火台传递警报,直到位于最南侧的总部。

以图2中的长城为例,负责守卫的四个烽火台用蓝白圆点示意,最南侧的总部用红色圆点示意。如果红色星形标示的地方出现敌情,将被哨兵们发现并沿红色折线将警报传递到总部。当然,就这个例子而言只需两个烽火台的协作,但其他位置的敌情可能需要更多。

然而反过来,即便这里的4个烽火台全部参与,依然有不能覆盖的(黄色)区域。

图 2

另外,为避免歧义,我们在这里约定,与某个烽火台的视线刚好相切的区域都认为可以被该烽火台所监视。以图3中的长城为例,若A、B、C、D点均共线,且在D点设置一处烽火台,则A、B、C以及线段BC上的任何一点都在该烽火台的监视范围之内。

图 3

好了,倘若你是秦始皇的太尉,为不致出现更多孟姜女式的悲剧,如何在保证长城安全的前提下,使消耗的民力(建造的烽火台)最少呢?

输入格式:

输入在第一行给出一个正整数N(3 ≤ N 105​​),即刻画长城边缘的折线顶点(含起点和终点)数。随后N行,每行给出一个顶点的xy坐标,其间以空格分隔。注意顶点从南到北依次给出,第一个顶点为总部所在位置。坐标为区间[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;
}
View Code

 

 

posted @ 2019-03-14 22:28  Albert_liu  阅读(453)  评论(0编辑  收藏  举报