第三次招新(2025)题解

题目链接:https://ac.nowcoder.com/acm/contest/125829
【邀请码:1214acm】

A.神秘问号

题目大意:题目要求我们在一个字符串中找出最长连续问号(?)的长度,以及这种最长连续问号序列出现的次数
具体实现参照代码
AC代码

#include <stdio.h>
#define int long long
const int N = 1e6;
void solve() {
    char s[N + 1];
    scanf("%s", s);
    int mx = 0;    // 最长长度
    int sum = 0;   // 记录临时长度
    int t = 0;     // 出现次数
    for(int i = 0; s[i] != '\0'; i++) {
        if(s[i] == '?') {
            sum++;
            if(sum == mx)  // 长度相同,出现次数加一
                t++;
            if(sum > mx) { // 更长,更新最长长度,出现次数设为一
                t = 1;
                mx = sum;
            }
        } else {           // 不是问号,临时长度归零
            sum = 0;
        }
    }
    printf("%lld %lld", mx, t);
}

signed main() {
    int t = 1;
    //scanf("%lld",&t);
    while(t--) solve();
    return 0;
}
B.刷题

这个问题本质上是将一个26进制数(但不是标准的26进制)转换为10进制数。
映射关系:A=1, B=2, C=3, …, Z=26

计算该字符对应的数值:s[i] - 'A' + 1

从右到左,每一位的权重是26的幂次

第一位:权重 = 26^0 = 1,第二位:权重 = 26^1 = 26,第三位:权重 = 26^2 = 676

具体实现参照代码
AC代码

#include<stdio.h>
#include<string.h>
#define int long long
const int N = 1e6;

void solve() {
    char s[N + 1];
    scanf("%s", s);
    int l = strlen(s);
    int t = 1;     // 当前权重,初始为26^0 = 1
    int sum = 0;   // 累加结果
    // 从右到左遍历字符串
    for(int i = l - 1; i >= 0; i--) {
        // 计算当前字符对应的数值并乘以权重
        sum += (s[i] - 'A' + 1) * t;
        // 更新权重为下一位的权重
        t *= 26;
    }
    printf("%lld", sum);
}

signed main() {
    int t = 1;
    while(t--) solve();
    return 0;
}
C.魔法矩阵

题目大意:题目要求我们找出最少需要施法几次,才能让n×n矩阵中的所有整数都变得相同。每次魔法可以选择矩阵内任意个相同的整数变成任意整数。

思路:每次都选取n*n的矩阵
要让所有数字相同,我们需要将不同值的数字逐步统一
最少施法次数 = 矩阵中不同数字的种类数 - 1

#include <stdio.h>
#include <stdbool.h>
#define int long long
const int N = 1e7;
void solve()
{
    int n;
    scanf("%lld",&n);
    bool f[N + 1];  // 用于标记数字是否出现过
    int sum = 0;    // 统计不同数字的种类数
    // 遍历矩阵
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            int t;
            scanf("%lld",&t);
            // 如果数字t第一次出现,sum加1
            if(f[t]++ == 0)
                sum++;
        }
    }
    // 最少施法次数 = 不同数字种类数 - 1
    printf("%lld",sum-1);
}

signed main()
{
    int t = 1;
    while(t--)solve();
    return 0;
}
D.⚪石

题目要求判断每个给定正整数的因数中是否包含质数,如果包含则输出"Yes"和最大的质因数,否则输出"No"。
数据范围较小(n ≤ 10000,最多50组测试数据),我们可以采用直接遍历的方法解决。

编写一个函数判断一个数是否为质数
遍历1到n的所有数,检查是否为n的因数
在所有因数中筛选出质数,并记录最大的质因数

AC代码

#include <stdio.h>
#include <stdbool.h>
#define int long long
const int N = 1e7;

// 判断x是否为质数
bool met(int x) {
    if(x == 1)
        return 0;
    if(x == 2)
        return 1;
    for(int i = 2; i*i <= x; i++) {
        if(x % i == 0)
            return 0;
    }
    return 1;
}

void solve() {
    int n;
    scanf("%lld", &n);
    bool f = 0;   // 标记是否存在质因数
    int sum = 0;  // 记录最大的质因数
    
    // 遍历1到n,查找n的因数
    for(int i = 1; i <= n; i++) {
        if(n % i == 0) {  // i是n的因数
            if(met(i)) {  // i是质数
                f = 1;    // 标记存在质因数
                sum = i;  // 记录质因数(从小到大遍历,最后记录的是最大的)
            }
        }
    }
    
    // 输出结果
    if(f) {
        printf("Yes %lld\n", sum);
    } else {
        printf("No\n");
    }
}
signed main() {
    int t = 1;
    scanf("%lld", &t);  // 读取测试数据组数
    while(t--) solve(); // 处理每组数据
    return 0;
}
E.斜线矩阵

题目要求构造一个n×n的方阵,将数字1到n²按照特定的斜线规律填入矩阵中。
有多种构造方法,这里提供一种

首先填充主对角线及其下方的斜线
然后填充主对角线上方的斜线

#include<stdio.h>
#define int long long 
const int N = 100+10;
void solve(){
    int n;
    scanf("%lld",&n);
	int a[N][N];
    int ans = 1; // 从1开始填充
    
    // 第一阶段:填充主对角线及其下方
    for(int k = n; k >= 1; k--){
        for(int i = k, j = 1; i <= n && j <= n; i++, j++){
            a[i][j] = ans++; // 填充数字并递增
        }
    }
    
    // 第二阶段:填充主对角线上方
    for(int k = 2; k <= n; k++){
        for(int i = 1, j = k; i <= n && j <= n; i++, j++){
            a[i][j] = ans++; // 填充数字并递增
        }
    }
    
    // 输出结果
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++)
            printf("%lld ", a[i][j]);
        printf("\n");
    }
}

signed main(){
    int t = 1;
    while(t--) solve();
    return 0;
}
F.金字塔

题目要求计算一个数字金字塔的顶部数字。金字塔的构建规则是:

  1. 最底层(第1层)有n个整数
  2. 每一层的数字比下一层少1个
  3. 第k层(k>1)的第i个数字等于第k-1层的第i个和第i+1个数字之和
  4. 需要计算金字塔顶部的数字对10^9+7取模的结果

我们不需要显式地构建整个金字塔,而是可以使用原地更新的方法:

用一个数组存储当前层的所有数字
从底层开始,逐层向上计算
每次计算上一层时,用当前层的相邻数字之和替换原数组
最终数组的第一个元素就是金字塔顶部的数字

#include<stdio.h>
#define int long long 
const int mod = 1e9 +7;
const int N = 3e3+10;

void solve() {
    int n;
    scanf("%lld", &n);
    int a[N];
    // 读取底层数字并取模
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        a[i] %= mod;
    }
    // 从底层向上逐层计算
    for(int k = n; k >= 1; k--) {
        for(int i = 1; i <= k-1; i++) {
            a[i] = (a[i] + a[i + 1]) % mod;
        }
    }
    printf("%lld\n", a[1]);
}

signed main() {
    int t = 1;
    scanf("%lld", &t);
    while(t--) solve();
    return 0;
}
G.神秘问好plus

题目询问的是有几组不同的连续x个问号

该题暴力能写,但是题解给出差分写法

1、一个长度为L的集群会贡献L个长度为1的子集群、L-1个长度为2的子集群…
2、通过差分数组a[1]++和a[L+1]–,一次遍历完成所有长度统计
3、前缀和计算后b[x]直接表示长度为x的集群数量

AC代码

#include <stdio.h>
#define int long long
const int N = 1e6;

void solve() {
    char s[N + 1];
    scanf("%s", s);
    int t = 0;
    int sum = 0;    // 记录当前连续问号长度
    int a[N] = {0}; // 差分数组
    int b[N] = {0}; // 结果数组
    
    // 第一步:遍历字符串,构建差分数组
    for(int i = 0; s[i] != '\0'; i++) {
        if(s[i] == '?') {
            sum++;
            a[1]++;        // 所有长度≥1的集群数量+1
            a[sum + 1]--;  // 长度≥sum+1的集群数量-1
        } else {
            sum = 0;       // 遇到非问号,重置计数器
        }
    }
    
    // 第二步:计算前缀和,得到各长度集群的实际数量
    for(int i = 1; i < N; i++) {
        b[i] = b[i-1] + a[i];
    }
    
    // 第三步:处理查询
    int n;
    scanf("%lld", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &t);
        printf("%lld\n", b[t]);
    }
}

signed main() {
    int t = 1;
    while(t--) solve();
    return 0;
}
H.智慧果园采摘系统

这是一个经典的博弈论问题,属于"取石子游戏"的变种。题目要求判断在双方都采取最优策略的情况下,先手玩家是否能获胜。
先给出AC代码

#include <stdio.h>
#define int long long
const int N = 1e6;

void solve() {
    int n,p,q;
    scanf("%lld%lld%lld",&n,&p,&q);
    int m = n%(p+q);//主要影响
    if(m<=q)
        printf("win");
    else
        printf("lose");
}

signed main() {
    int t = 1;
    while(t--) solve();
    return 0;
}
  • n % (p+q) <= q时,先手必胜
  • n % (p+q) > q时,先手必败

当苹果数量为k*(p+q)时,
如果先手拿x个,后手可以拿(p+q-x)个,苹果数再次成为(k-1)*(p+q)最终,当苹果数为p+q时,先手拿x个,后手拿(p+q-x)个获胜。

如果n % (p+q) = m

  • 0 < m ≤ q时,先手可以直接拿m个苹果,使剩余苹果数为k*(p+q),让对手处于必败位置.
  • m = 0时,等价于m = p+q,先手可以拿p个,让对手面对(k-1)*(p+q) + q,通过后续策略获胜.
  • m > q时,无论先手拿多少(p到q个),后手都能调整策略使先手始终面对m > q的局面.
I.井字棋

bfs,鉴于c语言版的bfs太过于难写给出cpp版的
想进一步了解联系这个学长在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const vector<vector<int>> target = {
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9}
};

const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
int sx = -1, sy = -1;

// 将3x3数组哈希为一个整数
long long hax(const vector<vector<int>>& a) {
	long long res = 0;
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			res = res * 10 + a[i][j];
		}
	}
	return res;
}

int bfs(vector<vector<int>> a) {
	unordered_set<long long> v;
	queue<tuple<vector<vector<int>>, int, int, int>> q;
	
	q.emplace(a, sx, sy, 0);
	v.insert(hax(a));
	
	while (!q.empty()) {
		auto [cur, x, y, step] = q.front();
		q.pop();
		
		if (cur == target) {
			return step;
		}
		
		for (int i = 0; i < 4; i++) {
			int nx = x + dx[i];
			int ny = y + dy[i];
			if (nx < 0 || nx >= 3 || ny < 0 || ny >= 3) continue;
			
			vector<vector<int>> next = cur;
			swap(next[x][y], next[nx][ny]);

			long long h = hax(next);
			if (!v.count(h)) {
				v.insert(h);
				q.emplace(next, nx, ny, step + 1);
			}
		}
	}
	return -1;
}

void solve() {
	vector<vector<int>> a(3, vector<int>(3));
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			char c;
			cin >> c;
			if (c == 'x') {
				a[i][j] = 9,sx=i,sy=j;
			} else {
				a[i][j] = c - '0';
			}
		}
	}
	
	int ans = bfs(a);
	cout << ans << endl;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	solve();
	return 0;
}
J.文本检索中的模式匹配任务

KMP模板题,可自行搜索学习

#include <stdio.h>
#include <string.h>
#define N 1000000

void solve()
{
	char a[N], b[N];
	scanf("%s %s", a, b);
	int n = strlen(b);
	int m = strlen(a);
	int nxt[N]={0};
	for(int i = 1, j = 0; i < n; i++){
		while(j && b[i] != b[j]){
			j = nxt[j-1];
		}
		if(b[i]==b[j])
			j++;
		nxt[i]=j;
	}
	int cnt = 0;
	int s[N]={0};
	for(int i = 0,j = 0; i < m; i++){
		while(j && a[i] != b[j])
			j = nxt[j-1];
		if(a[i] == b[j])
			j++;
		if(j == n){
			s[cnt++] = i - j + 2;
			j = nxt[j-1];
		}
	}
    if(cnt==0){
        printf("-1");
        return ;
    }
    printf("%d\n",cnt);
	for(int i = 0; i < cnt; i++){
		printf("%d ",s[i]);
	}
	
}

signed main()
{
	int t = 1;
//	cin >> t;
	while(t--)solve();
	return 0;
}
K.谦让的艺术

通过分析可以发现,最优分法是让a, b, c尽可能接近

如果有n个花生,令p = n / 3,q = n % 3
最优分法是:a = p - 1, b = p, c = p + q + 1

定义dp[i]表示当有i个花生时,小北在最优策略下最多能获得的花生数量。

AC代码

#include <stdio.h>
int dp[1000001]={0};
void solve()
{
    int n;
    scanf("%d",&n);
	printf("%d\n",dp[n]);
}

signed main(){    
    //预处理不然会超时
    for(int i = 6; i<=1000000; i++){
        int q = i % 3;
        int p = i / 3;
        dp[i] = dp[p + q + 1] + p - 1;
    }
    
	int t = 1;
    scanf("%d",&t);
	while(t--)solve();
	return 0;
}
### 关于CCPC 2025 山东省赛题解 目前尚未找到与 CCPC 2025 山东省赛相关的具体题解内容。根据已有的信息,CCPC(中国大学生程序设计竞赛)的历年比赛题解通常会在官方比赛结束后不久发布,可能以博客、论坛帖子或竞赛组织方的官方文档形式存在。如果 CCPC 2025 山东省赛已经结束但题解尚未公开,建议关注以下资源以获取最题解: - **CCPC 官方网站**:这是获取正式比赛题解和相关资料的首选渠道。 - **洛谷、牛客网等在线评测平台**:这些平台经常会有用户上传的比赛题解和代码。 - **各大高校 ACM/ICPC 训练队博客**:许多高校会针对 CCPC 比赛撰写详细的题解分析。 在等待官方题解的同时,可以参考以往的比赛题解来推测可能的题目类型和解法。例如,2021 年 CCPC 山东省赛中的一道构造题[^1]提供了以下思路:将矩阵 \( A \) 和 \( B \) 中矩阵 \( C \) 的位置为 1 的部分直接置为 1,而对矩阵 \( C \) 中为 0 的位置,则保证 \( A \) 和 \( B \) 的值相反。类似地,2024 年 CCPC 山东省赛中的一道构造题[^2]展示了如何通过特定规则生成矩阵。 以下是基于构造题常见模式的一个示例代码框架,适用于类似问题: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 505; int n, m; int a[MAXN][MAXN], b[MAXN][MAXN], c[MAXN][MAXN]; void solve() { cin >> n >> m; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { char ch; cin >> ch; c[i][j] = ch - '0'; if (c[i][j]) { a[i][j] = b[i][j] = 1; } else { a[i][j] = 0; b[i][j] = 1; } } } // 调整特殊位置 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (!c[i][j]) { a[i][j] = !b[i][j]; } } } // 输出结果 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cout << a[i][j]; } cout << endl; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cout << b[i][j]; } cout << endl; } } int main() { solve(); } ``` 此代码实现了矩阵 \( A \) 和 \( B \) 的构造逻辑,并确保满足给定条件。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值