2021 广东省程序设计竞赛(GDCPC)

本文回顾了一次线上程序设计竞赛经历,分享了解题策略,包括签到题L.LeagueofLegends、几何思维I.IndustrialNuclearWater、博弈与贪心算法应用,以及优先队列、贪心算法和数学知识的应用。作者反思了自己的表现,对未来提出学习提升的计划。
摘要由CSDN通过智能技术生成

回顾

本想有线下的比赛,未曾想因为疫情,最后还是线上举行了,临时借用一个会议室打的比赛,匆匆忙忙的过程中夹杂着大家对比赛的热情,等这么一个比赛也真的是等了很久。

比赛开始,首先看到的是A题,An Easy Problem,原想着噢,会不会这场比赛的签到就是A,看了下数据范围,诶,还是先通读一遍吧,看了看榜单,L题怎么过那么多人,输入描述:No input. 懂了,这才是签到题,马上抛开A题了,最后是qyg和xy过了这道题

qyg说看看这个博弈,然后xy说J好像是完全背包,qyg跟xy就讨论这个去了,我看博弈,刚开始也是理解错题意了(人为的想当然了)自己附加了一个条件,样例过了,结果wa了,结果我拉着qyg说,我给你分析一遍,你看一下过程有没有问题,qyg看了看题说:来吧。我开始分析,这样这样(省略 n 2 \frac{n}{2} 2n 个字) 我知道我错在哪了,改完,然后继续分析(省略 n 2 \frac{n}{2} 2n 个字)我感觉这样子应该没问题了,qyg:可以,我:那我交了?终于AC了,有点小激动。虽然这时候时间已经不多了,但是后面也想着,估计摸不到奖了,就不想了,就继续做吧。

(插播一个强烈谴责:临近期末,某王姓志愿者在复习计组,我在打比赛码题的过程中,他问我:老师说计组考什么?虽然当时我没有回他什么,但其实心里面已经骂了他千百遍了。 ^v^ [doge])

qyg和xy继续讨论着J题,J题交了几发,还是,qyg说,I题是个几何,是你的,我就去开I题了,大体的思想是对的,但是我理解成一个封闭体了,结果没过。xy说:J题我试试BFS吧,最后一发就过了。最后I题还是差点时间,总的来说,其实省赛的题目不算难,就自己太菜了,交了那么多发罚时,痛定思痛,思路来的太慢,刷的题太少,暑假当好好学习,希望接下来的比赛加加油吧!

比赛结束之后,打球的打球,吃饭的吃饭…

感想

其实这个比赛算是正式接触ACM的第一个比赛。说实话,真的挺激动的,还好最后是有个铜,虽然含金量不高,但至少在这方面能拿个奖了,还挺开心的,感谢队友 qyg 和 xysm 带我飞,感谢志愿者 (尤其是某王姓志愿者,这次比赛结束了,但这才是刚刚踏上acm的道路,希望我们能在这强者之林留下我们浅薄的脚印…,继续加油吧!

L. League of Legends(签到)

题意:有3个队伍进行战斗,1个队伍 VS 另一个队伍,获胜的和另外一个没参加的队伍进行战斗,直到出现某一支队伍失败次数达到2次。
求:进行战斗的场次 的期望值

一直1v1,直到出现某一支队伍失败次数达到2次,就结束。
那么失败次数达到2次的情况有:
(-1, -1, -2) ,(0, -1, -2) 有两种情况,所以答案就是:
3 × 0.5 + 4 × 0.5 = 3.5(直接PHP输出 [doge])

I. Industrial Nuclear Water (几何思想)

其实这道题呢就是一个简单的高中解析几何的思想。
有两个点 A ( x 1 , y 1 , z 1 )          B ( x 2 , y 2 , z 2 ) A(x_1, y_1, z_1) \;\;\;\;B(x_2, y_2, z_2) A(x1,y1,z1)B(x2,y2,z2)
有6个障碍曲面: 1000 ∣ x ∣ = y 2 + z 2 、 1000 ∣ y ∣ = x 2 + y 2 、 1000 ∣ z ∣ = x 2 + y 2 1000|x|=y^2+z^2、1000|y|=x^2+y^2、1000|z|=x^2+y^2 1000x=y2+z21000y=x2+y21000z=x2+y2

首先 不用管是什么曲面,知道就是一个面就完了
其次 为什么是6个障碍曲面呢?
1000 ∣ x ∣ = y 2 + z 2 1000|x|=y^2+z^2 1000x=y2+z2 包括两个曲面 1000 x = y 2 + z 2 1000x = y^2+z^2 1000x=y2+z2 − 1000 x = y 2 + z 2 -1000x = y^2+z^2 1000x=y2+z2

依题意:如果这2个点在同一个区域就输出Yes,换言之,这2个点不被这6个曲面中任意1个曲面分割,则输出Yes

判断曲面是否分割2个点
如果:
1000 x 1 < y 1 2 + z 1 2 1000x_1<y_1^2+z_1^2 1000x1<y12+z12 1000 x 2 < y 2 2 + z 2 2 1000x_2<y_2^2+z_2^2 1000x2<y22+z22 则证明A、B点被曲面 1000 x = y 2 + z 2 1000x=y^2+z^2 1000x=y2+z2 分割
如果:
1000 x 1 < y 1 2 + z 1 2 、 1000 x 2 < y 2 2 + z 2 2 1000x_1<y_1^2+z_1^2、1000x_2<y_2^2+z_2^2 1000x1<y12+z121000x2<y22+z22 或者 1000 x 1 > y 1 2 + z 1 2 、 1000 x 2 > y 2 2 + z 2 2 1000x_1>y_1^2+z_1^2、1000x_2>y_2^2+z_2^2 1000x1>y12+z121000x2>y22+z22 那么说明A,B两点在曲面 1000 x = y 2 + z 2 1000x=y^2+z^2 1000x=y2+z2 的同一侧,即没有分割

综上:依次判断一下即可

#include<bits/stdc++.h>
#define int long long
using namespace std;
int check(int a, int b, int c) { 
    if (1000 * c < a * a + b * b) return true;
    else return false;
}
int ok(int x1, int y1, int z1, int x2, int y2, int z2) {
    int flag = 1;
    if (check(x1, y1, z1) != check(x2, y2, z2)) flag = 0;
    if (check(x1, y1, -z1) != check(x2, y2, -z2)) flag = 0;
    if (check(x1, z1, y1) != check(x2, z2, y2)) flag = 0;
    if (check(x1, z1, -y1) != check(x2, z2, -y2)) flag = 0;
    if (check(z1, y1, x1) != check(z2, y2, x2)) flag = 0;
    if (check(z1, y1, -x1) != check(z2, y2, -x2)) flag = 0;
    return flag;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int _;
    cin >> _;
    while (_--) {
        int x1, y1, z1, x2, y2, z2;
        cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
        if (ok(x1, y1, z1, x2, y2, z2)) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}

G. Good Game, GG(一道不算博弈的博弈)

题意:A和B玩一场游戏,给定n个整数。
A有两种操作:
① 选一个奇数,将其分成两个整数
② 拿掉一个1
B只能有1种操作:选择一个偶数并且将这个偶数拆分成两个数

A为先手,判断谁是winner。

既然是博弈,那么就是找对应最优的操作

A最优的操作是:假设当前数是x,将奇数依次分解成x-2、2
如果分解出2的话,B如果再将2进行分解,那么只能分解成1、1,A的操作次数就多2次,A赚了。所以可以这么分解

所以A的操作次数是: a [ i ] / 2 + 1 a[i]/2 + 1 a[i]/2+1

B最优的操作是:假设当前数是x,将偶数进行分解,不要分解出奇数,只能是分解成两个偶数,

所以B的操作次数是: a [ i ] / 2 − 1 a[i]/2-1 a[i]/21

简单举例:
在这里插入图片描述
⚠ 记得开long long ⚠
Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
int a[N];
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int _;
    cin >> _;
    while (_--) {
        int n;
        cin >> n;
        int A = 0, B = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (a[i] % 2 == 1) A += a[i] / 2 + 1;
            else B += a[i] / 2 - 1;
        }
        if (A <= B) cout << "Bob" << endl;
        else cout << "Alice" << endl;
    }
    return 0;
}

J. Jerry(BFS)

题意:只能移动 完全平方数长度 的距离,问从0开始到x这个数,最少需要移动多少次。

先预处理100000以内的完全平方数,然后从0开始bfs,就可以找到最短距离了。

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int v[500], d[N];
bool st[N];
int cnt;
void init() {
    for (int i = 1; i * i <= 1e5; i++) v[++cnt] = i * i;
}

void bfs() {
    queue<int> q;
    q.push(0);
    st[0] = true;
    while(!q.empty()) {
        auto t = q.front();
        q.pop();
        for (int i = 1; i <= cnt; i++) {
            if (v[i] + t <= 1e5 && !st[v[i] + t]) {
                st[t + v[i]] = true;
                d[t + v[i]] = d[t] + 1;
                q.push(t + v[i]);
            }
            if (t - v[i] >= 0 && !st[t - v[i]]) {
                st[t - v[i]] = true;
                d[t - v[i]] = d[t] + 1;
                q.push(t - v[i]);
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    init();
    bfs();
    int t;
    cin >> t;
    while (t--) {
        int x;
        cin >> x;
        cout << d[x] << endl;
    }
    return 0;
}

A. An Easy Problem(优先队列)

求第k大的值,n,m的范围比较大,所以可以用优先队列存权值,每次要取出最大的一个,去k次,就是第k大,即答案。

#include<bits/stdc++.h>
#define x first
#define y second
#define int long long 
using namespace std;
typedef pair<int, int> PII;
int n, m, k;
priority_queue<PII> q;
vector<int> a, b;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) q.push({i * m, i});
    k--;
    while (k--) {
//         cout << q.top().x << ' ' << q.top().y << endl;
        auto t = q.top();
        q.pop();//弹出k次
        t.x -= t.y;
        q.push(t);
    }
    int ans = q.top().x;
    cout << ans << endl;
    return 0;
}

D. Double(贪心)

题意:有n个人,排成一排,进行格斗游戏,依照规则,求能获得胜利的人有多少个。

题解:
先存一个最大战斗力的值maxx
依次枚举每一个人,当前这个人跟左方的人格斗;
如果a[i] > a[i-1]那么就能赢
如果a[i] == a[i -1]也认定能赢
赢了的能量值 × 2
一直到a[i] < a[i - 1] 或者 一直能赢到最左边

同理进行与右边的人进行格斗

如果期间出现能量值大于maxx,那么存在可以赢的可能。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500010;
int a[N], b[N];
vector<int> ans;
signed main()
{
    int n;
    cin >> n;
    int maxx = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        maxx = max(maxx, a[i]);
    }
    
    for (int i = 1; i <= n; i++) {
        int t = a[i];
        int l = i, r = i;
        while (t < maxx) {
            int f = 0;
            if (t >= a[r + 1] && r + 1 <= n) {
                t *= 2;
                r++;
                f = 1;
            }
            if (t >= a[l - 1] && l > 1) {
                t *= 2;
                l--;
                f = 1;
            }
            if (!f) break;
        }
        if (t >= maxx) ans.push_back(i);
    }
    int len = ans.size();
    cout << len << endl;
    for (int i = 0; i < len; i++) cout << ans[i] << ' ';
    cout << endl;
    return 0;
}

F. Fake Math Problem(数学知识)

待续…

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值