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

该博客详细解析了GPLT团体程序设计天梯赛中L2级别的27道题目,涉及算法和数据结构,包括最短路、链表操作、二叉搜索树、回文子串、红包分配等。博客提到了各种解题策略,如优先队列、空间换时间、结构体排序、并查集等,并指出部分题目存在的陷阱和解题注意事项。
摘要由CSDN通过智能技术生成

更新了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]);
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值