GPLT團體程序設計天梯賽題解L2部分

更新了27題,還剩5題

00x

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;
}

01x

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;
}

02x

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;
}

03x

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章