【算法题解】2022年第四届河南省CCPC大学生程序设计竞赛(喜提银牌)

本文回顾了2022年河南省第四届CCPC大学生程序设计竞赛的经历,重点介绍了五道题目的解题思路与技巧,包括构造法、贪心算法、枚举法及DFS搜索等,分享了团队合作与解决问题的心得。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

比赛题目已上传到CF:2022 CCPC Henan Provincial Collegiate Programming Contest

比赛排名:Live: 2022年河南省第四届CCPC大学生程序设计竞赛 | RankLand (algoux.org)

喜提银牌一枚

image-20221003173821486

A. Mocha 上小班啦

这道题我队友从A题开始往后看,直接发现是道签到题,上去就开写,为了防止罚时,我们一起检查了一遍,不希望在小事在出错。最后一发A了。哈哈。

思路:构造

  • 如果n >10,你们肯定不存在,因为要保证数位中各个数字不相同。
  • 如果n <= 10
    • n = 1,答案就是0
    • n > 1,答案是就是10234…

比赛时AC代码

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

signed main()
{
    int n;
    cin >> n;
    if (n > 10)
        cout << -1 << endl;
    else
    {
        if (n == 1)
        {
            cout << 1 << endl;
        }
        else
        {
            cout << "10";
            for (int i = 2; i < n; i++)
            {
                cout << i;
            }
            cout << endl;
        }
    }
    return 0;
}

E. Serval 的俳句

过了一会发现E题过了很多,觉得肯定是道签到题,然后队长让我看,我上去一看发现还真是。

思路:贪心

首先,我们需要现在字符串从前到后找到5个相同字符,然后再再剩下的字符串中找7个字符,再然后在剩下的找5个相同字符,最后就找到了一个正确结果。

为啥这样保证一定正确呢:当时我想的是如果我从字符串的第一位开始找,如果某个字母出现了5次,那么它就一定是最优的,并且只用到了整个字符串最短的部分,那么剩下的同理。

比赛时AC代码

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

int cnt[26];

signed main()
{
    int n;
    string s;
    cin >> n >> s;

    string res = "";
    int ok = 0;
    for (int i = 0; i < n; i++)
    {
        cnt[s[i] - 'a']++;
        if (cnt[s[i] - 'a'] == 5 and ok == 0)
        {
            ok = 1;
            res += string(5, s[i]);
            for (int j = 0; j < 26; j++)
                cnt[j] = 0;
        }
        else if (cnt[s[i] - 'a'] == 7 and ok == 1)
        {
            ok = 2;
            res += string(7, s[i]);
            for (int j = 0; j < 26; j++)
                cnt[j] = 0;
        }
        else if (cnt[s[i] - 'a'] == 5 and ok == 2)
        {
            ok = 3;
            res += string(5, s[i]);
            break;
        }
    }
    if (ok == 3)
        cout << res << endl;
    else
        cout << "none" << endl;
    return 0;
}

F. 集合之和

刚开始看这道题的时候感觉这道肯定是个签到提,但是这个题我们当时都犯浑了,一直在想着怎么找规律,但找的规律都不对,一开始发现了所有奇数都可以构造出来,答案是1 2 3...。然后去想怎么构造偶数的,但发现2和4都构造不出来,是不是有其他偶数也构造不出来,然后去尝试6,8,10…,等等,就这样过了一个多小时还没写出来,但是都有点崩溃了,我当时内心都已经不能平静下去了,但已经过了两三百人了,自己不可能写不出来吧,然后队友构造出来了偶数8的构造为1 2 3 5,然后我就想着看看以这个为突破口,然后突然发现它和奇数的规律好像,都是把最后一位加了1,然后我就去测试10,发现就是这样,当时激动的快哭了。

1 2 3 4

2 3 4 5 6 7 8

1 2 3 5

2 3 4 5 6 7 8 10

然后交了一发A了。激动坏了。差点银牌都没了,就卡在这一道签到题上了。

比赛时AC代码

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

signed main()
{
    int n;
    cin >> n;
    if (n & 1)
    {
        int m = (n + 1) / 2;
        cout << m << endl;
        for (int i = 0; i < m; i++)
        {
            cout << i << " ";
        }
        cout << endl;
    }
    else
    {
        if (n == 2 or n == 4)
            cout << -1 << endl;
        else
        {
            int m = n / 2;
            cout << m << endl;
            for (int i = 1; i < m; i++)
            {
                cout << i << " ";
            }
            cout << m + 1 << endl;
        }
    }
    return 0;
}

G. Mocha 上大班啦

这道题也是签到题,队长先发现了这道题,看过的人也挺多,然后发现和概率有关,顿时就犯了难,因为关于概率的问题都特别难。但是当时已经过了几十个人了,就想着不可能太难。最后发现概率是个幌子,一点没用。这道题和概率没有任何关系。纯粹是迷惑人的。最后队长一发A了。不得不说这题真是妙啊。

思路:枚举

题目的本意是将n个字符串想与,最后统计字符串中有多少个1,直接枚举即可。

比赛时AC代码

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

string s[1010];
int cnt[4010];
signed main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= n; i++)
    {
        cin >> s[i];
        for (int j = 1; j <= m; j++)
        {
            if (s[i][j - 1] == '1')
                cnt[j]++;
        }
    }

    int q;
    cin >> q;
    while (q--)
    {
        int a, b, l, r, p;
        cin >> a >> b >> l >> r >> p;
    }

    int res = 0;
    for (int i = 1; i <= m; i++)
    {
        if (cnt[i] == n)
            res++;
    }
    cout << res << endl;

    return 0;
}

H. 旋转水管

这题是队长首先看到的,我俩都觉得这题不难,肯定能写,但当时只有一个人过,并且很多人都wa了,心想不可能这么难吧,然后就开始写,首先想的是Acwing里的AcWing 1131. 拯救大兵瑞恩这道题,想着还记录状态,但写完了之后提交wa了,然后发现光记录每个格子的状态有没有经历过不行,因为每个水管必须只能更改一次方向,而且只能走一次,所以不能记录状态,然后队长重写了一遍,但越来越复杂,写了四百多行,最后提交还是wa了,然后我突然想到bfs不行,拿dfs可不可以,然后计算了一下复杂度,最后发现由于水管的特殊形态,所以最多只有几种走法,大约是O(n)的复杂度,所以一定不会超时,然后我就提醒队长让他用dfs写,然后十分钟不到就写完并提交A了,我们当时贼激动,太艰难了,终究是过了,一下上到银牌了。

思路:DFS

每次搜的时候记录每个位置有没有走过,如果走过就不能在走,如果出界了也就不能在走了。走完之后还要回溯回来,为了换个路径寻找。

  • 遇到I:只能从上一个过来的方向直走
  • 遇到L: 从上一个过来的方向向它的两边走

比赛时AC代码

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

const int N = 1e5 + 10;
char g[3][N];
bool st[3][N];
int n = 4, m, ex, ey;
// 1 是往下走,2是往左走,3是往上走,4是往右走
bool dfs(int x, int y, int k)
{
    if (x == 3 and y == ey)
        return true;
    if (x < 1 or x > 2 or y < 1 or y > m or st[x][y])
        return false;
    st[x][y] = true;
    bool t;
    if (g[x][y] == 'I')
    {
        if (k == 1)
            t = dfs(x + 1, y, 1);
        else if (k == 2)
            t = dfs(x, y - 1, 2);
        else if (k == 3)
            t = dfs(x - 1, y, 3);
        else
            t = dfs(x, y + 1, 4);
    }
    else
    {
        if (k == 1 or k == 3)
        {
            t = (dfs(x, y - 1, 2) || dfs(x, y + 1, 4));
        }
        else if (k == 2 or k == 4)
        {
            t = (dfs(x - 1, y, 3) || dfs(x + 1, y, 1));
        }
    }
    st[x][y] = false;
    return t;
}

void solve()
{
    cin >> m >> ex >> ey;
    for (int i = 1; i <= 2; i++)
    {
        string s;
        cin >> s;
        for (int j = 1; j <= m; j++)
        {
            st[i][j] = false;
            g[i][j] = s[j - 1];
        }
    }

    if (dfs(1, ex, 1))
        cout << "YES" << endl;
    else
        cout << "NO" << endl;
}

signed main()
{
    freopen("in.in", "r", stdin);
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;

    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

比赛的时候只写了这五道题,剩下的能补的尽快补上。

### 关于2023河南CCPC大赛G题的解析 对于2023第五届中国大学生程序设计竞赛CCPC河南省赛中的G题,虽然具体的题目描述未被供,但从常见的比赛模式来看,这类问题通常涉及字符串处理、动态规划或其他算法技巧。以下是基于已有信息和常见题型推测的一种可能解法。 #### 可能的题目背景与目标 假设该题的目标是验证某个特定子串是否存在重复出现的情况,则可以采用如下方法实现: 1. **输入读取**: 获取待检测的字符串 `s`。 2. **子串取**: 取出字符串的前100个字符作为基准子串 `s2`。 3. **查找匹配**: 判断在原字符串中除去初始位置外是否有其他地方再次出现了这个子串。 4. **输出结果**: 如果不存在额外匹配则返回 `"YES"` 表明无重复;反之若有重叠则输出 `"NO"`。 下面是对应的C++代码示例[^1]: ```cpp #include <bits/stdc++.h> using namespace std; string s, s2; int main() { cin >> s; s2 = s.substr(0, 100); if (s.find(s2, 100) == string::npos) cout << "YES"; else cout << "NO"; return 0; } ``` 此段代码通过标准库函数实现了上述逻辑流程。其中 `.substr()` 方法用于获取指定长度开头片段,而 `.find()` 函数负责定位除起始处之外任何可能出现的位置。 另外需要注意的是,在实际比赛中可能会遇到更复杂的约束条件或者更高的性能需求,因此建议参赛者熟悉各种数据结构以及优化技术来应对不同场景下的挑战。 #### 动态规划的应用实例 如果问题是关于矩阵路径计数或者其他类似的二维数组操作,那么动态规划可能是解决问题的有效途径之一。例如下面这段针对某类简单网格移动问题进行了状态转移方程定义并加以简化存储空间版本展示: 设 dp[i][j] 表示到达第i行第j列格子的方法总数, 当只允许向右或向下走时有关系式: dp[i][j]=dp[i−1][j]+dp[i][j−1] 为了节省内存消耗可进一步压缩成一维形式表示当前层的状态依赖于上一层的结果即可完成计算过程[^2]. ```cpp vector<long long> pre(m+1), cur(m+1); for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++){ if(i==0 && j==0){ cur[j]=1;//起点初始化为1种方式 } else{ long long up=(i>=1)?pre[j]:0; long long left=(j>=1)?cur[j-1]:0; cur[j]=(up+left)%mod; } } swap(pre,cur);//更新到下一行继续迭代 } cout<<pre[m]<<'\n'; ``` 以上仅作为一个例子说明如何运用动态规划解决某些类型的组合数学相关题目,并不一定适用于具体G题情况。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值