2026CCCC第三次模拟赛 部分题解
链接:https://pintia.cn/problem-sets/2043859722891493376/exam/overview
这里主要针对天梯赛,其他比赛不太适用,为了自己也为了帮助他人复习以及更稳的备赛,我就做了一个简单的题解。
基础题部分
这一场的 \(L1\)-\(L7\) 我就不写了,感觉就是简单的模拟。
L8:古风排版
中国的古人写文字,是从右向左竖向排版的。本题就请你编写程序,把一段文字按古风排版。
输入格式:
输入在第一行给出一个正整数 \(N\)(\(< 100\)),是每一列的字符数。第二行给出一个长度不超过1000的非空字符串,以回车结束。
输出格式:
按古风格式排版给定的字符串,每列 \(N\) 个字符(除了最后一列可能不足 \(N\) 个)。
简单讲述:
这道题本身很简单,这里主要想科普一个 \(C++\) 字符串这一块的工具:\(getline()\)
这个工具主要是读取一个字符串,和正常 \(cin\) 的不同之处在于 \(cin\) 遇到空格 / 制表符 / 换行符会停止读取,而 \(getline\) 会读取整行(含空格),直到遇到换行符为止。所以在读取一些包含空格的字符串中 \(getline\) 是很有用的。
格式以及使用可以参考下面代码,需要注意的是,在用 \(getline()\) 读取字符串之前如果有 \(cin\) 的读取,因为 \(cin\) 读取的时候换行符会残留在输入缓冲区中,此时直接调用 \(getline\) 会读取到空行。所以正常使用 \(getline\) 之前需要加上 $ cin.ignore() $。
格式: \(getline\)(\(cin\), 字符串变量)
讲完之后至少在读取上没有什么问题了,之和根据题意写即可。
参考AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
char ans[N][N];
int n;
int len,id;
string s;
int main(){
cin>>n;
cin.ignore();
getline(cin,s);
for(int i=0;i<s.size();i++){
ans[len][id]=s[i];
id++;
if(id==n){
id=0;
len++;
}
}
while(id>0&&id<n){
ans[len][id]=' ';
id++;
}
if(id==0)
len--;
for(int i=0;i<n;i++){
for(int j=len;j>=0;j--)
cout<<ans[j][i];
cout<<'\n';
}
return 0;
}
进阶题部分
L9:字符串维修店
给定一个长度为 \(2n\) 的合法括号序列(包含(),[], {}),为每一对匹配的括号定义整齐度为:该括号内部含有的同类型完整括号对数减去不同类型完整括号对数,要求找出整齐度最大的括号对,若有并列则选左括号下标最小的,输出其整齐度与下标。
合法括号序列:从左到右遍历括号串时,每个右括号都能匹配到最近的、没被用过的、同类型的左括号,全程不会出现匹配不上、类型错配或括号交叉的情况,最终所有括号都能正确闭合。
简单讲述:
由于这个是合法括号序列,因此对于任意的字符 \(s_{i}\) ,必然唯一存在一个字符 \(s_{j}\) 与其配对,所以可以根据:每一个右括号都可以匹配上一个最近的、未被匹配的左括号 这句话,可以利用栈来为每个\(s_{i}\) 为左括号的进行配对,设 \(idx_{i}\) 为 \(s_{i}\) 为左括号时候与 \(s_{i}\) 匹配的右括号下标, \(idx_{i}=0\) 代表 \(s_{i}\) 为右括号。
然后对于每一个左括号的下标 \(i\) ,都有一个区间 \((i,idx_{i})\) ,我们要计算 \((i,idx_{i})\) 中和 \(s_{i},s_{idx_{i}}\) 一样的括号对数,可以根据和 \(s_{i}\) 一样类型的左括号数量进行前缀和处理计算。
参考AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int n;
string s;
stack<int>c;
int idx[N];
int s1[N],s2[N],s3[N];
int main(){
cin>>n>>s;
s=" "+s;
for(int i=1;i<=2*n;i++){
if(s[i]=='('||s[i]=='['||s[i]=='{')
c.push(i);
else{
idx[c.top()]=i;
c.pop();
}
}
for(int i=1;i<=2*n;i++){
s1[i]+=s1[i-1];
s2[i]+=s2[i-1];
s3[i]+=s3[i-1];
if(s[i]=='(')
s1[i]++;
if(s[i]=='[')
s2[i]++;
if(s[i]=='{')
s3[i]++;
}
int ans=-1,l=-1,r=-1;
for(int i=1;i<=2*n;i++){
if(idx[i]==0)
continue;
int res=0;
if(s[i]=='(')
res=2*(s1[idx[i]-1]-s1[i])-(idx[i]-i-1)/2;
if(s[i]=='[')
res=2*(s2[idx[i]-1]-s2[i])-(idx[i]-i-1)/2;
if(s[i]=='{')
res=2*(s3[idx[i]-1]-s3[i])-(idx[i]-i-1)/2;
if(res>ans){
ans=res;
l=i;
r=idx[i];
}
}
cout<<ans<<' '<<l<<' '<<r<<'\n';
return 0;
}
L10:乐器寄存系统
题意略
简单讲述:
感觉就是比较简单的 \(map\) 使用,可以自行学习 \(map\)。
参考AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
map<string,int>sum;
map<string,int>num;
map<pair<string,string>,bool>st;
int main(){
cin>>n>>m;
while(n--){
string s;
int x;
cin>>s>>x;
sum[s]+=x;
}
while(m--){
string a,b,c;
cin>>a>>b>>c;
if(a=="store"){
if(!sum[c]){
cout<<"Can't store "<<c<<'\n';
}else if(num[c]==sum[c]){
cout<<"No place for "<<c<<'\n';
}else{
cout<<c<<" stored"<<'\n';
num[c]++;
st[{b,c}]=true;
}
}else{
if(!sum[c]){
cout<<"We don't have "<<c<<" here"<<'\n';
}else if(!st[{b,c}]){
cout<<c<<" not found"<<'\n';
}else{
st[{b,c}]=false;
num[c]--;
cout<<c<<" taken"<<'\n';
}
}
}
return 0;
}
L11:终末地工业:供电中继连接问题
题意略
简单讲述:
注意到中继站当且仅当只在固定的层数节点上,然后\(dfs\)模拟即可。
参考AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int n,k;
vector<int>eg[N];
int ans;
void dfs(int u,int fa,int len){
if(len==k){
ans+=k;
len=0;
}
bool st=false;
for(auto v:eg[u]){
if(v==fa)
continue;
dfs(v,u,len+1);
st=true;
}
if(!st)
ans+=len;
}
int main(){
cin>>n>>k;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
eg[u].push_back(v);
eg[v].push_back(u);
}
dfs(1,-1,0);
cout<<ans<<'\n';
return 0;
}
L12:摩天楼谜题解数统计
题意略
简单讲述:
简单的对每个位置进行$ dfs$ 模拟即可,由于每行每列每个数都只能出现一次,根据这个进行剪枝,然后判断处理即可。
参考AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
string s[N];
int cnt;
bool st1[N][N],st2[N][N];
int a[N][N],ans[N][N];
int l1[N],l2[N],r1[N],r2[N];
bool check(){
for(int i=1;i<=n;i++){
int c1,c2,c3,c4;
c1=c2=c3=c4=0;
int i1,i2,i3,i4;
i1=i2=i3=i4=0;
for(int j=1;j<=n;j++){
if(a[i][j]>i1){
i1=a[i][j];
c1++;
}
if(a[j][i]>i2){
i2=a[j][i];
c2++;
}
}
for(int j=n;j>=1;j--){
if(a[i][j]>i3){
i3=a[i][j];
c3++;
}
if(a[j][i]>i4){
i4=a[j][i];
c4++;
}
}
l1[i]=c1;
r1[i]=c2;
l2[i]=c3;
r2[i]=c4;
}
for(int i=1;i<=n;i++){
if(a[0][i]&&r1[i]!=a[0][i])
return false;
if(a[n+1][i]&&r2[i]!=a[n+1][i])
return false;
if(a[i][0]&&l1[i]!=a[i][0])
return false;
if(a[i][n+1]&&l2[i]!=a[i][n+1])
return false;
}
return true;
}
void dfs(int x,int y){
if(x>n){
if(check()){
cnt++;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans[i][j]=a[i][j];
}
}
for(int i=1;i<=n;i++)
if(!st1[x][i]&&!st2[y][i]){
st1[x][i]=st2[y][i]=true;
a[x][y]=i;
if(y==n)
dfs(x+1,1);
else
dfs(x,y+1);
st1[x][i]=st2[y][i]=false;
}
}
int main(){
cin>>n;
for(int i=0;i<n+2;i++)
cin>>s[i];
for(int i=0;i<n+2;i++)
for(int j=0;j<n+2;j++)
a[i][j]=(s[i][j]-'0');
dfs(1,1);
cout<<cnt<<'\n';
if(cnt==1){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<ans[i][j];
if(j!=n)
cout<<' ';
}
cout<<'\n';
}
}else{
cout<<"Bad problem -_-"<<'\n';
}
return 0;
}
#提高题部分
L14:那就别担心了
题意略
简单讲述:
简单的从起点 \(A\) 开始出发遍历,标记所有经过的节点 \(v\) 为 \(st_{v}=1\)。然后遍历到结束节点 \(B\) 后开始往回遍历,往回遍历的途中标记所有经过的节点,处理即可。
参考AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1050;
int n,m;
int s,e;
ll num[N];
vector<int>eg[N];
ll d[N],st[N];
void dfs(int u,int e){
if(u==e){
num[e]=1;
d[e]=1;
return ;
}
if(st[u])
return ;
st[u]=true;
for(auto v:eg[u]){
dfs(v,e);
num[u]+=num[v];
if(num[v]>0){
d[u]=1;
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
eg[u].push_back(v);
}
cin>>s>>e;
dfs(s,e);
bool sr=false;
for(int i=1;i<=n;i++){
if(st[i]&&i!=e&&d[i]==0){
sr=true;
}
}
cout<<num[s]<<' ';
if(sr)
cout<<"No";
else
cout<<"Yes";
return 0;
}
剩下两道难度都比较高,感觉对于天梯赛拿分而言帮助不大,就不写了。

浙公网安备 33010602011771号