题目链接: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层)有n个整数
- 每一层的数字比下一层少1个
- 第k层(k>1)的第i个数字等于第k-1层的第i个和第i+1个数字之和
- 需要计算金字塔顶部的数字对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;
}
1837

被折叠的 条评论
为什么被折叠?



