GPLT团体程序设计天梯赛题解L2部分

L2-001 紧急救援 (25 分)

复杂了一些的最短路,用优先队列对查找最小 dis 的过程做了优化。dis[i] 表示起点到 i 的最短路长度,pre[i] 表示 i 的前驱,cnt[i] 表示到达 i 的最短路条数,sum[i] 表示最多的救援队数目。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define N 505

using namespace std;

int n, m, s, d, x, y, z;
int num[N], path[N][N], dis[N], pre[N], cnt[N], sum[N];
bool vis[N];

struct Node {
    int key, value;
    Node(int k, int v) {
        key = k;
        value = v;
    }
    bool operator<(const Node &a) const { return value > a.value; }
};

void dijkstra() {
    pre[s] = -1;
    cnt[s] = 1;
    sum[s] = num[s];
    dis[s] = 0;
    priority_queue<Node> q;
    q.push(Node(s, 0));
    while (!q.empty()) {
        int k = q.top().key;
        q.pop();
        if (vis[k]) continue;
        vis[k] = true;
        for (int i = 0; i < n; ++i) {
            if (!vis[i]) {
                int cur_dis = dis[k] + path[k][i];
                int cur_sum = sum[k] + num[i];
                if (cur_dis < dis[i]) {
                    dis[i] = cur_dis;
                    pre[i] = k;
                    cnt[i] = cnt[k];
                    sum[i] = cur_sum;
                } else if (cur_dis == dis[i]) {
                    cnt[i] += cnt[k];
                    if (cur_sum > sum[i]) {
                        sum[i] = cur_sum;
                        pre[i] = k;
                    }
                }
                q.push(Node(i, dis[i]));
            }
        }
    }
    printf("%d %d\n", cnt[d], sum[d]);
    stack<int> stk;
    int k = d;
    while (k != -1) {
        stk.push(k);
        k = pre[k];
    }
    printf("%d", stk.top());
    stk.pop();
    while (!stk.empty()) {
        printf(" %d", stk.top());
        stk.pop();
    }
    printf("\n");
}

int main() {
    memset(path, INF, sizeof(path));
    memset(dis, INF, sizeof(dis));
    scanf("%d%d%d%d", &n, &m, &s, &d);
    for (int i = 0; i < n; ++i) scanf("%d", &num[i]);
    for (int i = 0; i < m; ++i) {
        scanf("%d%d%d", &x, &y, &z);
        path[x][y] = path[y][x] = z;
    }
    dijkstra();
    return 0;
}

L2-002 链表去重 (25 分)

典型的空间换时间,如果按照是否重复然后一个一个删除重复的,肯定会超时。vis 数组做标记,没重复就放到第一个队列里,重复的放到第二个队列里,最后读一遍两个队列就好,复杂度差不多是 O(n)

#include <bits/stdc++.h>
using namespace std;

struct Node {
    int data, next;
};

int head, n, a, b, c;
vector<Node> node(100005);
vector<bool> vis(10005);
queue<int> q1, q2;

int main() {
    scanf("%d%d", &head, &n);
    while (n--) {
        scanf("%d%d%d", &a, &b, &c);
        node[a].data = b;
        node[a].next = c;
    }
    for (int i = head; i != -1; i = node[i].next) {
        if (!vis[abs(node[i].data)]) {
            vis[abs(node[i].data)] = true;
            q1.push(i);
        } else
            q2.push(i);
    }
    int pos = q1.front();
    q1.pop();
    printf("%05d %d ", pos, node[pos].data);
    while (!q1.empty()) {
        pos = q1.front();
        q1.pop();
        printf("%05d\n%05d %d ", pos, pos, node[pos].data);
    }
    printf("-1\n");
    if (!q2.empty()) {
        pos = q2.front();
        q2.pop();
        printf("%05d %d ", pos, node[pos].data);
        while (!q2.empty()) {
            pos = q2.front();
            q2.pop();
            printf("%05d\n%05d %d ", pos, pos, node[pos].data);
        }
        printf("-1");
    }
    return 0;
}

L2-003 月饼 (25 分)

基本的结构体排序加贪心,但是这个 C++ 里的小数真是奇怪啊,不知道精度发生了什么神奇的错误,下面第一个代码提交一直有一组数据过不了,把 int 换成 double 就对了???。。。??

#include <bits/stdc++.h>
using namespace std;

struct Node {
    int x, y;
    double z;
    bool operator<(const Node &node) const { return z > node.z; }
} a[1005];

int n, d;

int main() {
    cin >> n >> d;
    for (int i = 0; i < n; ++i) cin >> a[i].x;
    for (int i = 0; i < n; ++i) {
        cin >> a[i].y;
        a[i].z = a[i].y * 1.0 / a[i].x;
    }
    sort(a, a + n);
    double sum = 0;
    for (int i = 0; i < n; ++i) {
        if (d >= a[i].x) {
            d -= a[i].x;
            sum += a[i].y;
        } else {
            sum += d * a[i].z;
            break;
        }
    }
    cout << fixed << setprecision(2) << sum;
    return 0;
}

正确代码,太坑了吧:

#include <bits/stdc++.h>
using namespace std;

struct Node {
    double x, y, z;  // x, y 换成 double
    bool operator<(const Node &node) const { return z > node.z; }
} a[1005];

int n;
double d;  // d 换成 double

int main() {
    cin >> n >> d;
    for (int i = 0; i < n; ++i) cin >> a[i].x;
    for (int i = 0; i < n; ++i) {
        cin >> a[i].y;
        a[i].z = a[i].y / a[i].x;
    }
    sort(a, a + n);
    double sum = 0;
    for (int i = 0; i < n; ++i) {
        if (d >= a[i].x) {
            d -= a[i].x;
            sum += a[i].y;
        } else {
            sum += d * a[i].z;
            break;
        }
    }
    cout << fixed << setprecision(2) << sum;
    return 0;
}

L2-004 这是二叉搜索树吗? (25 分)

本以为上完数据结构的课应该就会各种树了,结果发现自己还是太年轻。这题按照二叉搜索树的性质建树,先试试是不是二叉搜索树,再试试是不是二叉搜索树的 “镜像”,如果最后能建成就顺便输出后序遍历。

#include <bits/stdc++.h>

using namespace std;

int n, pre[1005], post[1005], cnt = 0;
bool flag;

void build(int l, int r) {
    if (l > r) return;
    int i = l + 1, j = r;
    if (!flag) {
        while (i <= r && pre[i] < pre[l]) ++i;
        while (l < j && pre[j] >= pre[l]) --j;
    } else {
        while (i <= r && pre[i] >= pre[l]) ++i;
        while (l < j && pre[j] < pre[l]) --j;
    }
    if (i - j != 1) return;
    build(l + 1, j);
    build(i, r);
    post[cnt++] = pre[l];
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) scanf("%d", &pre[i]);
    build(0, n - 1);
    if (cnt != n) {
        flag = true;
        cnt = 0;
        build(0, n - 1);
    }
    if (cnt != n)
        printf("NO\n");
    else {
        printf("YES\n");
        printf("%d", post[0]);
        for (int i = 1; i < cnt; ++i) printf(" %d", post[i]);
        printf("\n");
    }
    return 0;
}

L2-008 最长对称子串 (25 分)

马拉车算法模板题,c(center)表示中间点,r(right)表示回文串的右边界。具体算法思路不说了,还有需要理解的就是 r 关于 c 的对称点为 2 * c - i

#include <bits/stdc++.h>

using namespace std;

int manacher(string s) {
    string str = "$#";
    for (int i = 0; i < s.size(); ++i) {
        str.push_back(s[i]);
        str.push_back('#');
    }
    int c = 0, r = 0;
    vector<int> p(str.size());
    for (int i = 1; i < p.size(); ++i) {
        p[i] = r > i ? min(p[2 * c - i], r - i + 1) : 1;
        while (str[i - p[i]] == str[i + p[i]]) ++p[i];
        if (r - c + 1 < p[i]) {
            c = i;
            r = i + p[i] - 1;
        }
    }
    // return s.substr((2 * c - r) / 2, r - c);
    return r - c;
}

int main() {
    string s;
    getline(cin, s);
    cout << manacher(s);
    return 0;
}

L2-009 抢红包 (25 分)

结构体排序,输入的钱是分为单位,最后输出时除以 100.0 得到元。用结构体储存每个人的编号,收入,抢到红包数。

#include <bits/stdc++.h>
#define N 10005

using namespace std;

struct Node {
    int key, sum, cnt;
    Node() : sum(0), cnt(0) {}
} a[N];

int n, k, x, y;

bool cmp(Node a, Node b) {
    if (a.sum == b.sum) {
        if (a.cnt == b.cnt)
            return a.key < b.key;
        return a.cnt > b.cnt;
    }
    return a.sum > b.sum;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        a[i].key = i;
        scanf("%d", &k);
        while (k--) {
            scanf("%d%d", &x, &y);
            a[i].sum -= y;
            a[x].sum += y;
            ++a[x].cnt;
        }
    }
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1; i <= n; ++i)
        printf("%d %.2f\n", a[i].key, a[i].sum / 100.0);
    return 0;
}

L2-010 排座位 (25 分)

并查集,如果两个人是朋友关系就合并,否则,用 flag 数组标记两人是敌人。最后直接判断一下输出就好。

#include <bits/stdc++.h>
#define N 105

using namespace std;

int n, m, k, x, y, z;
int f[N], _rank[N] = {0}, flag[N][N] = {false};

int _find(int x) {
    return x == f[x] ? x : f[x] = _find(f[x]);  // 路径压缩
}

void _union(int x, int y) {
    int f1 = _find(x), f2 = _find(y);
    if (f1 != f2) {
        if (_rank[f1] < _rank[f2])  // 按秩合并
            f[f1] = f2;
        else {
            f[f2] = f1;
            if (_rank[f1] == _rank[f2])
                ++_rank[f1];
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++i)
        f[i] = i;
    while (m--) {
        scanf("%d%d%d", &x, &y, &z);
        if (z == 1)
            _union(x, y);
        else
            flag[x][y] = flag[y][x] = true;
    }
    while (k--) {
        scanf("%d%d", &x, &y);
        if (_find(x) == _find(y)) {
            if (!flag[x][y])
                printf("No problem\n");
            else
                printf("OK but...\n");
        } else {
            if (!flag[x][y])
                printf("OK\n");
            else
                printf("No way\n");
        }
    }
    return 0;
}

L2-012 关于堆的判断 (25 分)

肝了我一晚,怎么提交都是部分正确,我是真的无奈,直到看到有个题解:“用线性调整 heap 的话是不对的,题目要求是一个一个插入空的 MinHeap,所以只能插入,上浮,插入,上浮,插入,上浮。。。”,然而我试了一晚线性建堆。

思路很粗暴,先建堆(思路就是上面那句话,采用上浮的方式,加一个数浮一次),然后用 stringstream 把单词分割出来装进 vector,剩下就是简单判断。

#include <bits/stdc++.h>
#define N 1005

using namespace std;

int n, m, a[N];
string s;
stringstream ss;
vector<string> v;

void swim(int i) {
    int parent = (i - 1) / 2;
    if (parent >= 0 && a[parent] > a[i]) {
        swap(a[i], a[parent]);
        swim(parent);
    }
}

int toInt(string &s) {
    stringstream ss;
    ss << s;
    int x;
    ss >> x;
    return x;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        swim(i);
    }
    cin.get();  // 吸收回车
    for (int i = 0; i < m; ++i) {
        getline(cin, s);
        ss << s;
        while (ss >> s) v.push_back(s);
        if (v[3] == "root") {
            if (toInt(v[0]) == a[0])
                cout << "T\n";
            else
                cout << "F\n";
        } else if (v[4] == "siblings") {
            int p1 = find(a, a + n, toInt(v[0])) - a;
            int p2 = find(a, a + n, toInt(v[2])) - a;
            if (((p1 - 1) >> 1) == ((p2 - 1) >> 1))
                cout << "T\n";
            else
                cout << "F\n";
        } else if (v[3] == "parent") {
            int p1 = find(a, a + n, toInt(v[0])) - a;
            int p2 = find(a, a + n, toInt(v.back())) - a;
            if ((p2 - 1) >> 1 == p1)
                cout << "T\n";
            else
                cout << "F\n";
        } else {
            int p1 = find(a, a + n, toInt(v[0])) - a;
            int p2 = find(a, a + n, toInt(v.back())) - a;
            if ((p1 - 1) >> 1 == p2)
                cout << "T\n";
            else
                cout << "F\n";
        }
        v.clear();
        ss.clear();
    }
    return 0;
}

L2-013 红色警报 (25 分)

很明显的并查集,第一次合并之后,统计一下有几个分支,然后失去城市就标记,重新初始化后再合并,比较当前分支数和上一次分支数的变化,分支变多了就说明产生了不连通的城市。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 505, maxm = 5005;

int n, m, k, x, cnt, tmp, u[maxm], v[maxm], f[maxn];
bool vis[maxn];

int Find(int x) { return f[x] == x ? x : f[x] = Find(f[x]); }

void Union(int x, int y) {
    int f1 = Find(x), f2 = Find(y);
    if (f1 != f2) f[f1] = f2;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) f[i] = i;  // 初始化
    for (int i = 0; i < m; ++i) {
        cin >> u[i] >> v[i];
        Union(u[i], v[i]);
    }
    cnt = 0;
    for (int i = 0; i < n; ++i)
        if (f[i] == i) ++cnt;  // 计算分支个数
    cin >> k;
    while (k--) {
        cin >> x;
        vis[x] = true;
        for (int i = 0; i < n; ++i) f[i] = i;  // 标记后重新合并
        for (int i = 0; i < m; ++i)
            if (!vis[u[i]] && !vis[v[i]]) Union(u[i], v[i]);
        tmp = 0;
        for (int i = 0; i < n; ++i)
            if (f[i] == i && !vis[i]) ++tmp;  // 计算没被标记的分支个数
        if (tmp > cnt)
            cout << "Red Alert: City " << x << " is lost!\n";
        else
            cout << "City " << x << " is lost.\n";
        cnt = tmp;
    }
    if (cnt == 0)
        cout << "Game Over.";
    return 0;
}

L2-014 列车调度 (25 分)

第一眼看到题目,先是想到了单调栈(单调递减栈),因为题目要求火车按递减顺序离开。后来发现想复杂了,答案只需要回答至少几条轨道,并不要求输出具体的哪个火车走哪条轨道。因此我们只需要关注栈顶是什么,所以可以就用一个数字来表示栈顶的元素而不需要栈。思路还是差不多。

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    set<int> s;  // 利用 set 自带排序的特性,方便筛选出最优的轨道
    int n, x;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> x;
        if (s.empty())
            s.insert(x);  // 新增第一条轨道
        else {
            set<int>::iterator it = s.upper_bound(x);  // upper_bound 返回第一个值大于 x 的迭代器
            if (it != s.end()) s.erase(it);  // 有符合要求的轨道就替换值,否则新增轨道
            s.insert(x);
        }
    }
    cout << s.size();
    return 0;
}

L2-015 互评成绩 (25 分)

盯着题目,想了半天,好歹是 L2 啊,怎么可能这么简单,肯定哪里设了坑,结果这题还真的就是这么简单,sort 几下就过了。

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int n, k, m;
    double sum;
    cin >> n >> k >> m;
    vector<double> res;
    vector<int> v(k);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < k; ++j)
            cin >> v[j];
        sort(v.begin(), v.end());
        sum = 0;
        for (int j = 1; j < k - 1; ++j)
            sum += v[j];
        res.push_back(sum / (k - 2));
    }
    sort(res.begin(), res.end());
    cout << fixed << setprecision(3) << res[n - m];
    for (int i = n - m + 1; i < n; ++i)
        cout << ' ' << fixed << setprecision(3) << res[i];
    return 0;
}

L2-016 愿天下有情人都是失散多年的兄妹 (25 分)

这题。。。怎么形容呢,本身不是太难,但是真的坑啊,第一次提交,就对了两个测试点。

然后以为递归的思路有问题,改了很久,才发现根本不是递归的问题,问题在 25,26 行,就缺了那两行,意思是题目的测试点可能需要判断父母能不能结婚,所以要把父母的性别也加上去:

#include <bits/stdc++.h>
using namespace std;

struct Node {
    char s;
    int f, m;  // father, mother
    Node() : f(-1), m(-1) {}
} a[100005];

int n, id, k, x, y;

bool dfs(int x, int y, int cnt) {
    if (cnt == 5) return true;  // 超过五代就不用管了
    if (x == -1 || y == -1) return true;  // 其中一方判断不了,直接给过
    if (x == y) return false;  // 五代之内出现相同的人,不过
    return dfs(a[x].f, a[y].f, cnt + 1) && dfs(a[x].m, a[y].m, cnt + 1) &&
           dfs(a[x].f, a[y].m, cnt + 1) && dfs(a[x].m, a[y].f, cnt + 1);
}

int main() {
    cin >> n;
    while (n--) {
        cin >> id;
        cin >> a[id].s >> a[id].f >> a[id].m;
        a[a[id].f].s = 'M';  // 坑
        a[a[id].m].s = 'F';  // 坑
    }
    cin >> k;
    while (k--) {
        cin >> x >> y;
        if (a[x].s == a[y].s)
            cout << "Never Mind" << endl;
        else if (dfs(x, y, 0))
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

L2-017 人以群分 (25 分)

算是 L2 里比较简单的题,直接 sort 就好了。

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    vector<int> v(n);
    for (int i = 0; i < n; ++i) cin >> v[i];
    sort(v.begin(), v.end());
    int m = n >> 1;
    if (n & 1)
        cout << "Outgoing #: " << m + 1 << endl;
    else
        cout << "Outgoing #: " << m << endl;
    cout << "Introverted #:" << m << endl;
    cout << "Diff = "
         << accumulate(v.begin() + m, v.end(), 0) -
                accumulate(v.begin(), v.begin() + m, 0)
         << endl;
    return 0;
}

L2-019 悄悄关注 (25 分)

直接按照题目描述找出没被关注且点赞数大于平均点赞数的。

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    int n, m, x, sum = 0;
    string s;
    cin >> n;
    map<string, bool> vis;  // 标记是否被关注
    map<string, int> cnt;  // 记录点赞数
    for (int i = 0; i < n; ++i) {
        cin >> s;
        vis[s] = true;
    }
    cin >> m;
    for (int i = 0; i < m; ++i) {
        cin >> s >> x;
        cnt[s] = x;
        sum += x;
    }
    sum /= m;
    bool flag = false;
    for (auto it : cnt) {
        if (!vis[it.first] && it.second > sum) {
            cout << it.first << endl;
            flag = true;
        }
    }
    if (!flag) cout << "Bing Mei You" << endl;
    return 0;
}

L2-020 功夫传人 (25 分)

第一次偷了懒,只记录了某人的师傅是谁,然后遇到 “得道者” 就往上查找,结果最后一个测试点超时了,估计是 pow 的地方太耗时间,错误代码

#include <bits/stdc++.h>
using namespace std;

int n, k, x;
double z, r, sum = 0;
vector<pair<int, int>> v;
vector<int> t(100001, -1);

int dfs(int x) { return t[x] == -1 ? 0 : dfs(t[x]) + 1; }

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> z >> r;
    r = 1 - r / 100;
    for (int i = 0; i < n; ++i) {
        cin >> k;
        if (k == 0) {
            cin >> x;
            v.push_back(pair<int, int>(i, x));
        } else {
            while (k--) {
                cin >> x;
                t[x] = i;
            }
        }
    }
    for (auto it : v) sum += z * pow(r, dfs(it.first)) * it.second;
    cout << (int)sum << endl;
    return 0;
}

然后换了个思路,记录下某人的所有徒弟,然后从上往下查找,终于全通过了:

#include <bits/stdc++.h>
using namespace std;

int n, k, x;
double z, r, sum = 0;
vector<vector<int>> v(100001);  // 存某人的所有徒弟
vector<int> flag(100001, -1);  // 标记是不是得道者

void dfs(int x, double cur) {
    if (flag[x] != -1)  // 如果是得道者
        sum += cur * flag[x];
    else  // 否则递归他的所有徒弟,并且功力要递减
        for (auto it : v[x]) dfs(it, cur * r);
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> z >> r;
    r = 1 - r / 100;
    for (int i = 0; i < n; ++i) {
        cin >> k;
        if (k == 0)
            cin >> flag[i];
        else {
            while (k--) {
                cin >> x;
                v[i].push_back(x);
            }
        }
    }
    dfs(0, z);
    cout << (int)sum << endl;
    return 0;
}

L2-021 点赞狂魔 (25 分)

很明显的 map,存放标签编号到出现次数的映射,最后直接 sort 排序。

#include <bits/stdc++.h>
using namespace std;

// 最近喜欢用pair代替结构体,分别存名字和标签出现次数
typedef pair<string, map<int, int>> Node;

int main() {
    ios::sync_with_stdio(false);
    int n, k, x;
    cin >> n;
    vector<Node> v(n);
    for (int i = 0; i < n; ++i) {
        cin >> v[i].first >> k;
        while (k--) {
            cin >> x;
            v[i].second[x]++;
        }
    }
    sort(v.begin(), v.end(), [](Node &a, Node &b) {
        if (a.second.size() == b.second.size()) {
            // 计算标签出现次数平均值
            int s1 = 0, s2 = 0;
            for (auto it : a.second) s1 += it.second;
            for (auto it : b.second) s2 += it.second;
            return s1 < s2;
        }
        return a.second.size() > b.second.size();
    });
    for (int i = 0; i < 3; ++i) {
        if (i < v.size())
            cout << v[i].first;
        else
            cout << '-';
        if (i != 2) cout << ' ';
    }
    return 0;
}

L2-022 重排链表 (25 分)

用数组模拟链表,然后遍历一次,把每个结点按顺序装到另一个数组,双指针法,一个从头往中间读,一个从尾往中间读,控制格式输出还是 C 语言简短些,C++ 要打一堆的 setfill, setw …

#include <bits/stdc++.h>
using namespace std;

struct Node {
    int data, next;
} node[100000];

int main() {
    int first, n, a, b, c;
    scanf("%d%d", &first, &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d%d%d", &a, &b, &c);
        node[a].data = b;
        node[a].next = c;
    }
    vector<int> v;
    for (int i = first; i != -1; i = node[i].next) v.push_back(i);
    printf("%05d %d ", v.back(), node[v.back()].data);
    int i = 0, j = v.size() - 2, m = v.size() / 2;
    while (i < m || j >= m) {
        if (i < m) {
            printf("%05d\n%05d %d ", v[i], v[i], node[v[i]].data);
            ++i;
        }
        if (j >= m) {
            printf("%05d\n%05d %d ", v[j], v[j], node[v[j]].data);
            --j;
        }
    }
    printf("-1\n");
    return 0;
}

L2-023 图着色问题 (25 分)

很基本的图的 m 着色问题,都不需要求出所有方案,只需要判断着色是否符合要求,唯一需要注意的就是使用到的颜色数需要刚好等于 K。

#include <bits/stdc++.h>
using namespace std;

int v, e, k, x, y, n;
bool path[505][505];
int color[505];
set<int> flag;  // 用 set 统计使用了几种颜色

bool judge() {
    flag.clear();
    for (int i = 1; i <= v; ++i) {
        flag.insert(color[i]);
        for (int j = 1; j <= v; ++j) {
            if (path[i][j] && color[i] == color[j]) return false;
        }
    }
    if (flag.size() != k) return false;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> v >> e >> k;
    for (int i = 1; i <= e; ++i) {
        cin >> x >> y;
        path[x][y] = true;
    }
    cin >> n;
    while (n--) {
        for (int i = 1; i <= v; ++i) cin >> color[i];
        if (judge())
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

L2-024 部落 (25 分)

很基础的并查集。

#include <bits/stdc++.h>
using namespace std;

int f[10005];

int Find(int x) { return x == f[x] ? x : f[x] = Find(f[x]); }

void Union(int x, int y) {
    int f1 = Find(x), f2 = Find(y);
    if (f1 != f2) f[f1] = f2;
}

int main() {
    ios::sync_with_stdio(false);
    for (int i = 1; i < 10005; ++i) f[i] = i;
    int n, k, x, y, flag = 0, cnt = 0, q;
    cin >> n;
    while (n--) {
        cin >> k;
        cin >> x;
        flag = max(flag, x);
        k--;
        while (k--) {
            cin >> y;
            flag = max(flag, y);
            Union(x, y);
            x = y;
        }
    }
    for (int i = 1; i <= flag; ++i)
        if (f[i] == i) ++cnt;
    cout << flag << ' ' << cnt << endl;
    cin >> q;
    while (q--) {
        cin >> x >> y;
        if (Find(x) == Find(y))
            cout << "Y\n";
        else
            cout << "N\n";
    }
    return 0;
}

L2-025 分而治之 (25 分)

当作邻接表做,题意就是需要判断最后是否所有点的连通度都是 0。

#include <bits/stdc++.h>
using namespace std;

int n, m, x, y, k, t, num[10005], temp[10005];
vector<vector<int>> v(10005);

bool judge() {
    for (int i = 1; i <= n; ++i)
        if (temp[i] > 0) return false;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    while (m--) {
        cin >> x >> y;
        num[x]++;
        num[y]++;
        v[x].push_back(y);
        v[y].push_back(x);
    }
    cin >> k;
    while (k--) {
        cin >> t;
        for (int i = 1; i <= n; ++i) temp[i] = num[i];
        while (t--) {
            cin >> x;
            temp[x] = 0;
            for (auto y : v[x]) temp[y]--;
        }
        if (judge())
            cout << "YES\n";
        else
            cout << "NO\n";
    }
    return 0;
}

L2-026 小字辈 (25 分)

有点像二叉树求高度,直接暴力深搜,v 数组存子结点的下标,d 数组存结点深度。

#include <bits/stdc++.h>
using namespace std;

int n, x;
vector<vector<int>> v(100005);
vector<int> d(100005);

int depth(int x) {
    int res = d[x];
    if (v[x].empty()) return res;
    for (int i = 0; i < v[x].size(); ++i) {
        d[v[x][i]] = d[x] + 1;
        res = max(res, depth(v[x][i]));
    }
    return res;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &x);
        v[x == -1 ? 0 : x].push_back(i);
    }
    int dep = depth(0);
    printf("%d\n", dep);
    bool flag = false;
    for (int i = 1; i <= n; ++i) {
        if (d[i] == dep) {
            if (flag) printf(" ");
            printf("%d", i);
            flag = true;
        }
    }
    return 0;
}

L2-027 名人堂与代金券 (25 分)

算是 L2 里比较简单的题,直接 sort 就能过了,就是输出有点麻烦。

#include <bits/stdc++.h>
using namespace std;

typedef pair<string, int> Node;

int main() {
    int n, g, k, sum = 0, num;
    string id;
    vector<Node> v;
    cin >> n >> g >> k;
    for (int i = 0; i < n; ++i) {
        cin >> id >> num;
        v.push_back(Node(id, num));
        if (num >= 60 && num < g)
            sum += 20;
        else if (num >= g && num <= 100)
            sum += 50;
    }
    cout << sum << endl;
    sort(v.begin(), v.end(), [](Node &a, Node &b) {
        if (a.second == b.second) return a.first < b.first;
        return a.second > b.second;
    });
    int i = 0, x = 1, cnt = 0;
    while (true) {
        cout << x << ' ' << v[i].first << ' ' << v[i].second << endl;
        ++cnt;
        if (i + 1 < v.size()) {
            if (v[i].second != v[i + 1].second) {
                x = cnt + 1;
                if (x > k) break;
            }
            ++i;
        } else
            break;
    }
    return 0;
}

L2-028 秀恩爱分得快 (25 分)

GPLT 的题。。。是真的恶心啊,没有之一,这题的坑点:

  • -0 算是女的,如果直接用数字输入,-0 会被当成 0,所以必须用字符串输入。
  • 如果 a 和 b 都没出现在任何一张照片中,直接默认他们就是亲密度最高的。
  • 最后需要注意一下就是,按照题意只需要注意 a 和 b 的亲密度,就是说没有 a 或 b 出现的照片就可以直接忽略了。

真的恶心啊。。。

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;

string s;
int n, m, k, x, a, b;
vector<vector<int>> photo(N);
vector<bool> sex(N);  // 这用true表示男,false表示女
map<int, double> ma, mb;  // 用map存亲密度关系
vector<pair<int, double>> va, vb;  // map本身不能排序,但是可以借助vector
vector<int> ra, rb;

void input(int &x) {
    cin >> s;
    int i = 0;
    if (s[0] == '-') i = 1;
    x = atoi(s.c_str() + i);  // ASCII to Int (c语言库函数)
    if (i == 0) sex[x] = true;
}

void output(int x, int y) {
    if (!sex[x]) cout << '-';
    cout << x << ' ';
    if (!sex[y]) cout << '-';
    cout << y << endl;
}

bool cmp(pair<int, double> &a, pair<int, double> &b) {
    // 优先按亲密度排序,亲密度相同的,按照编号排序
    return a.second == b.second ? a.first < b.first : a.second > b.second;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        cin >> k;
        while (k--) {
            input(x);
            photo[i].push_back(x);
        }
    }
    input(a);
    input(b);
    for (int i = 0; i < m; ++i) {
        double t = 1.0 / photo[i].size();
        // a或b出现在照片中才需要处理
        if (find(photo[i].begin(), photo[i].end(), a) != photo[i].end())
            for (auto it : photo[i])
                if (sex[a] != sex[it]) ma[it] += t;
        if (find(photo[i].begin(), photo[i].end(), b) != photo[i].end())
            for (auto it : photo[i])
                if (sex[b] != sex[it]) mb[it] += t;
    }
    if (ma.empty() && mb.empty()) {
        // 如果任何一张照片都没有a和b
        output(a, b);
        return 0;
    }
    for (auto it : ma) va.push_back(it);
    for (auto it : mb) vb.push_back(it);
    sort(va.begin(), va.end(), cmp);
    sort(vb.begin(), vb.end(), cmp);
    // 排序后把亲密度最高的(可能有多个)放到另外两个数组
    for (int i = 0; i < va.size(); ++i) {
        ra.push_back(va[i].first);
        if (i + 1 < va.size() && va[i].second > va[i + 1].second) break;
    }
    for (int i = 0; i < vb.size(); ++i) {
        rb.push_back(vb[i].first);
        if (i + 1 < vb.size() && vb[i].second > vb[i + 1].second) break;
    }
    // 最后就是输出
    if (find(ra.begin(), ra.end(), b) != ra.end() && find(rb.begin(), rb.end(), a) != rb.end())
        output(a, b);
    else {
        for (auto it : ra) output(a, it);
        for (auto it : rb) output(b, it);
    }
    return 0;
}

L2-029 特立独行的幸福 (25 分)

挺简单的一题,第一次提交,34 行那个变量忘记初始化错了一个测试点,卡了好久。。。

#include <bits/stdc++.h>
using namespace std;

bool vis[10005], flag[10005];  // vis 处理死循环,flag 处理依附
int cnt[10005];
vector<int> res;

int isPrime(int x) {
    if (x < 2) return 1;
    for (int i = 2; i <= sqrt(x); ++i)
        if (x % i == 0) return 1;
    return 2;
}

int main() {
    int a, b;
    cin >> a >> b;
    for (int i = a; i <= b; ++i) {
        int n = i;
        memset(vis, 0, sizeof(vis));
        while (n != 1) {
            int sum = 0;
            while (n) {
                sum += pow(n % 10, 2);
                n /= 10;
            }
            n = sum;
            if (vis[n]) break;
            vis[n] = flag[n] = 1;
            cnt[i]++;
        }
        if (n == 1) res.push_back(i);
    }
    bool has = false;
    for (auto it : res) {
        if (!flag[it]) {
            cout << it << ' ' << cnt[it] * isPrime(it) << endl;
            has = true;
        }
    }
    if (!has) cout << "SAD";
    return 0;
}

L2-030 冰岛人 (25 分)

首先,这题看起来和 L2-016 很像,但是有几点比较坑:

  • “五代之内” 指的是四代(自己 -> 爸爸 -> 爸爸的爸爸 -> 爸爸的爸爸的爸爸)
  • 查询并没有保证两个人是同辈。
  • 共同祖先(有的话)不能是任何一方的五代之内,比如一个是 8 代,另一个是 3 代,这就是不行的。
  • 最后就是,题目可能会变态地问你,某人能不能和他的祖先结婚。。。

第 6 个点不好过,不知道有没有什么更好的方法,我用 set 作判断花了三百多毫秒,题目限制四百毫秒,也差点没过,可能用 C 会好过一点。

#include <bits/stdc++.h>
using namespace std;

struct Node {
    bool sex;
    string father;
};

int n, m;
string a, b, c, d;
map<string, Node> mp;  // 记录性别和父亲
set<string> s1, s2;  // 把第一个人的五代内的亲属和自己装s1, 五代之外的亲属装 s2

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    while (n--) {
        cin >> a >> b;
        if (b.back() == 'm')
            mp[a].sex = 1;
        else if (b.back() == 'f')
            mp[a].sex = 0;
        else if (b.back() == 'n')
            mp[a] = {1, b.substr(0, b.size() - 4)};
        else
            mp[a] = {0, b.substr(0, b.size() - 7)};
    }
    cin >> m;
    while (m--) {
        cin >> a >> b >> c >> d;
        if (mp.find(a) == mp.end() || mp.find(c) == mp.end())
            cout << "NA" << endl;
        else if (mp[a].sex == mp[c].sex)
            cout << "Whatever" << endl;
        else {
            bool flag = 0;
            s1.clear(), s2.clear();
            string name = a;
            for (int i = 0; i < 4 && !name.empty(); ++i) {
                s1.insert(name);
                name = mp[name].father;
            }
            while (!name.empty()) {
                s2.insert(name);
                name = mp[name].father;
            }
            /* 经过上面的处理,s1里就是第一个人五代内的亲属和它自己,s2里就是第一个人的五代外的亲属
               然后用第二个人比较,如果第二个人五代内亲属与第一个人的重复(所有亲属和自己)就No
               如果第二个人的五代外亲属与第一个人的五代内亲属或第一个人自己重复 也是No
               剩下就是 yes,这里如果用两重for循环去遍历两人的所有亲属,第6个测试点过不了 */
            name = c;
            for (int i = 0; i < 4 && !name.empty(); ++i) {
                if (s1.find(name) != s1.end() || s2.find(name) != s2.end()) {
                    flag = 1;
                    break;
                }
                name = mp[name].father;
            }
            while (!flag && !name.empty()) {
                if (s1.find(name) != s1.end()) {
                    flag = 1;
                    break;
                }
                name = mp[name].father;
            }
            if (flag)
                cout << "No" << endl;
            else
                cout << "Yes" << endl;
        }
    }
    return 0;
}

L2-031 深入虎穴 (25 分)

这题主要就是没有给出入口,所以需要自己找到入口。能由其他门到达的肯定不是入口,所以只需要作下标记,最后剩下的那个没有任何门可以到达的就是入口。

#include <bits/stdc++.h>
using namespace std;

int n, k, x, res, cnt = -1;
vector<bool> flag(100005);
vector<vector<int>> v(100005);

void dfs(int x, int depth) {
    if (depth > cnt) {
        cnt = depth;
        res = x;
    }
    for (int i = 0; i < v[x].size(); ++i) dfs(v[x][i], depth + 1);
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> k;
        while (k--) {
            cin >> x;
            v[i].push_back(x);
            flag[x] = 1;  // 能到达 x, 标记
        }
    }
    int root = 1;
    while (flag[root]) ++root;  // 剩下到达不了的就是入口
    dfs(root, 0);
    cout << res;
    return 0;
}

L2-032 彩虹瓶 (25 分)

题目很长。。。其实问的就是有一个限制大小的栈辅助,能不能把给定序列按顺序排好。

#include <bits/stdc++.h>
using namespace std;

int n, m, k;
vector<int> v(1005);

bool judge() {
    stack<int> s;
    int cur = 1;  // 表示当前需要哪种编号的小球
    for (int i = 0; i < n;) {
        if (cur == v[i]) {
            // 如果序列里的这个数就是瓶子能装的
            ++cur;
            ++i;
        } else if (!s.empty() && cur == s.top()) {
            // 如果栈顶的数是瓶子能装的
            ++cur;
            s.pop();
        } else {
            // 都装不了只能暂时堆着,如果容量超了,return false
            if (s.size() == m) return false;
            s.push(v[i]);
            ++i;
        }
    }
    // 最后把栈里面剩下的也装进瓶子里
    while (!s.empty()) {
        if (cur == s.top()) {
            ++cur;
            s.pop();
        } else
            return false;
    }
    return true;
}

int main() {
    cin >> n >> m >> k;
    while (k--) {
        for (int i = 0; i < n; ++i) cin >> v[i];
        if (judge())
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/maolonglong/p/12513524.html