2024睿抗CAIP省赛

RC-u1 热҈热҈热҈

思路

按题意模拟即可,统计所有可以喝到雪碧的天数,因为星期四无法喝到雪碧的天数。

复杂度

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

代码实现

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

void solve()
{
    int n, w;
    cin >> n >> w;
    int cnt = 0, cnt1 = 0;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        if (x >= 35) {
            if (w == 4)
                cnt1++;
            else
                cnt++;
        }
        if (w == 7)
            w = 1;
        else
            w++;
    }
    cout << cnt << ' ' << cnt1;
}

int main()
{
    int T = 1;
    // cin>>T;
    while (T--) {
        solve();
    }
}

RC-u2 谁进线下了?

思路

同样也是按题意模拟,每读入第 i i i 个人的排名和杀敌数,根据排名计算出对应的得分,然后和杀敌数一起累加到数组下标为 i i i 的位置,最后对应输出即可。

复杂度

时空复杂度 O ( n ) O(n) O(n)

代码实现

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

// 根据排名计算分数
int f(int x)
{
    if (x == 1)
        return 12;
    if (x == 2)
        return 9;
    if (x == 3)
        return 7;
    if (x == 4)
        return 5;
    if (x == 5)
        return 4;
    if (x <= 7)
        return 3;
    if (x <= 10)
        return 2;
    if (x <= 15)
        return 1;
    return 0;
}

void solve()
{
    int n;
    cin >> n;
    int val[25] = { 0 };
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= 20; j++) {
            int p, t;
            cin >> p >> t;
            val[j] += f(p) + t;
        }
    }
    for (int i = 1; i <= 20; i++) {
        cout << i << ' ' << val[i] << '\n';
    }
}

int main()
{
    int T = 1;
    // cin>>T;
    while (T--) {
        solve();
    }
}

RC-u3 暖炉与水豚

思路

首先,对暖炉周围的位置进行标记。
接着,遍历整个矩阵,看是否存在未被标记的位置,且该位置上有温暖的水豚 ′ m ′ 'm' m
如果存在的话说明该水豚周围存在暖炉,枚举水豚周围八个方向的位置 ( x , y ) (x,y) (x,y),如果 ( x , y ) (x,y) (x,y) 的位置为 ′ . ′ '.' .,且 ( x , y ) (x,y) (x,y) 周围八个方向不存在很冷的水豚 ′ c ′ 'c' c,则该位置可以放置暖炉, ( x , y ) (x,y) (x,y) 加入答案集合。

细节

1,遍历方向可以使用方向数组,即用数组记录下某个方向的偏移量。
2,检查八个方向的位置 ( x , y ) (x,y) (x,y),可能存在重复,可以直接存到 s e t set set 里面,这样去重且排好序了。

复杂度

时空复杂度 O ( n 2 ) O(n^2) O(n2)

代码实现

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

const int N = 1e3 + 5;

int n, m;
string g[N];
int st[N][N];
int dx[] = { -1, -1, -1, 0, 0, 1, 1, 1 }, dy[] = { -1, 0, 1, -1, 1, -1, 0, 1 };

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> g[i];
        g[i] = ' ' + g[i];
        for (int j = 1; j <= m; j++) {
            if (g[i][j] == 'm') {
                // 标记暖炉周围八个方向
                for (int k = 0; k < 8; k++) {
                    st[i + dx[k]][j + dy[k]] = 1;
                }
            }
        }
    }
    set<array<int, 2>> ans;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            // 没有被暖炉'm' 覆盖到的 'w'
            if (!st[i][j] && g[i][j] == 'w') {
                for (int k = 0; k < 8; k++) {
                    // 枚举周围的位置(x,y)
                    int x = i + dx[k], y = j + dy[k];
                    if (x < 1 || x > n || y < 1 || y > m || g[x][y] != '.')
                        continue;
                    int flag = 1;
                    for (int k1 = 0; k1 < 8; k1++) {
                        int xi = x + dx[k1], yi = y + dy[k1];
                        if (xi < 1 || xi > n || yi < 1 || yi > m)
                            continue;
                        flag &= g[xi][yi] != 'c';
                        // 检查(x,y) 周围是否有'c'
                    }
                    if (flag)
                        ans.insert({ x, y });
                }
            }
        }
    }
    if (!ans.size()) {
        cout << "Too cold!\n";
        return;
    }
    for (auto it : ans) {
        cout << it[0] << ' ' << it[1] << '\n';
    }
}

int main()
{
    int T = 1;
    // cin>>T;
    while (T--) {
        solve();
    }
}

RC-u4 章鱼图的判断

思路

感觉题意有些问题,里面想找的子图实际上是极大连通子图,即无向图中不连接在一起的连通块。

赛时想的有点复杂,但是注意到一些性质可以简单不少。

注意到章鱼图实际上是一个无向基环树,会满足以下条件:
1,满足边数恰好为点数;
2,删去环上的链后,环上点的度数最多为 2 2 2

首先,对于每个连通块,可以统计该连通块的点数和连通块上点的度数和,因为度数和恰好为边数的两倍,所以就可以用点数,度数和判断是否满足条件 1 1 1

找到连通块的所有点可以用 b f s bfs bfs 或者 d f s dfs dfs,遍历连通块时,要标记该连通块上遍历到的点已经遍历过,防止下次从连通块另一个点开始遍历连通块。

如果满足条件 1 1 1,则对该连通块进行删链,删链可以用 K a h n Kahn Kahn 算法(进行拓扑排序的算法),因为这里是无向图,所以最开始是将度数为 1 1 1 的点入队,然后当点 u u u 出队时,就把点 u u u 的相邻节点 v v v 的度数减 1 1 1(表示删去点 u u u),如果点 v v v 的度数为 1 1 1,则将点 v v v 入队。

为方便处理,在代码实现的时候,直接用 s e t set set 存领接表,度数看 s e t set set 的大小,然后点 u u u 出队时,把点 u u u 所有相邻节点 v v v 的领接表中删去。

K a h n Kahn Kahn 算法执行结束后,遍历连通块的所有点,看是否存在点的度数(领接表大小)大于 2 2 2,如果存在则该连通块不是章鱼子图。

如果连通块是章鱼子图,还需要统计环上的点数,这步可以在 K a h n Kahn Kahn 算法执行的之后一并完成。因为在最开始的 b f s / d f s bfs/dfs bfs/dfs 中,找出了连通块的点集 a l l all all,而 K a h n Kahn Kahn 算法遍历到的点都是链上的点,所以可以把 K a h n Kahn Kahn 算法遍历到的点从 a l l all all 中删去(为了删去的复杂度低, a l l all all 也用 s e t set set 存储),最后 a l l all all 的大小即为环上的点数。

最后根据要求输出即可,如果章鱼子图唯一则输出环的大小,否则输出章鱼子图的大小。

复杂度

s e t set set 插入删除的时间复杂度都是 O ( log ⁡ n ) O(\log n) O(logn),因此总的时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),空间复杂度为 O ( n + m ) O(n+m) O(n+m)

代码实现

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

const int N = 1e5 + 5;

int n, m;
int st[N];
// st[u] 表示该点是否遍历过
set<int> h[N];

int cnt, num;
// cnt为章鱼子图数量,num为章鱼子图环的大小

void bfs(int x)
{
    int sum = 0;
    set<int> all;
    // all为连通块的点集
    queue<int> q;
    q.push(x);
    st[x] = 1;
    while (q.size()) {
        int u = q.front();
        q.pop();
        all.insert(u);
        sum += h[u].size();
        for (int v : h[u]) {
            if (!st[v]) {
                st[v] = 1;
                q.push(v);
            }
        }
    }
    // 点数恰好为边数
    if (sum == all.size() * 2) {
        cnt++;
        for (int i : all) {
            if (h[i].size() == 1) {
                q.push(i);
            }
        }
        while (q.size()) {
            int u = q.front();
            q.pop();
            all.erase(u);
            for (int v : h[u]) {
                h[v].erase(u);
                if (h[v].size() == 1) {
                    q.push(v);
                }
            }
            h[u].clear();
        }
        for (int i : all) {
            // 如果存在点度数大于2则不为章鱼子图
            if (h[i].size() > 2)
                return;
        }
        num = all.size();
    }
}

void solve()
{
    cin >> n >> m;
    cnt = num = 0;
    for (int i = 1; i <= n; i++) {
        st[i] = 0;
        h[i].clear();
    }
    while (m--) {
        int a, b;
        cin >> a >> b;
        h[a].insert(b);
        h[b].insert(a);
    }
    int ans = 0;
    memset(st, 0, sizeof(st));
    for (int i = 1; i <= n; i++) {
        if (!st[i]) {
            bfs(i);
        }
    }
    if (cnt != 1) {
        cout << "No " << cnt << '\n';
    } else {
        cout << "Yes " << num << '\n';
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
}

工作安排

思路

赛时没联想到该题可以类似背包进行 d p dp dp ,往暴力贪心方面想了。

f ( i , j ) f(i,j) f(i,j) 为安排前 i i i 个工作,截止时间恰好为 j j j 能取得的最大报酬。

注意到这里的状态表示涉及到截止时间的早晚问题,显然要先安排截止时间早的,所以要先按截止时间从小到大排序。

初始化:令所有状态默认为 0 0 0 即可,表示未获得任何报酬。

状态转移:
1,不安排第 i i i 个工作,则 f ( i , j ) = f ( i − 1 , j ) f(i,j) = f(i-1,j) f(i,j)=f(i1,j)
2,安排第 i i i 个工作,则 f ( i , j ) = f ( i − 1 , j − t ) + p f(i,j) = f(i-1,j-t)+p f(i,j)=f(i1,jt)+p t t t 为花费时间, p p p 为报酬),注意这种情况需要满足截止时间 j j j 不超过第 i i i 项工作的截止时间 d d d

最后的答案即为 f ( n , x ) ( 0 ≤ x ≤ d n ) f(n,x)(0 \le x \le d_n) f(n,x)(0xdn) 取得的最大值。

细节

注意直接开二维数组记录,空间复杂度会超,所以需要用滚动数组优化空间复杂度,和 01 01 01 背包的优化方法是一样的,计算的时候倒序计算即可。

复杂度

时间复杂度 O ( n ∗ d ) O(n*d) O(nd),空间复杂度 O ( d ) O(d) O(d)

代码实现

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

const int N = 5e3 + 5;

int n;
int f[N];
array<int, 3> a[N];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int t, d, p;
        cin >> t >> d >> p;
        a[i] = { d, t, p };
    }
    sort(a + 1, a + 1 + n);
    memset(f, 0, sizeof(f));
    for (int i = 1; i <= n; i++) {
        int t = a[i][1], d = a[i][0], p = a[i][2];
        for (int j = d; j >= t; j--) {
            f[j] = max(f[j], f[j - t] + p);
        }
    }
    int ans = 0;
    for (int i = 0; i < N; i++) {
        ans = max(ans, f[i]);
    }
    cout << ans << '\n';
}

int main()
{
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
}
  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值