2023CCPC题解
2023 年第五届河南省 CCPC 大学生程序设计竞赛
Problem A. 小水獭游河南

思路:从第二个字母开始往后依次判断,先判断从这个字母往后是不是回文串,若是则输出HE结束当前循环,若不是判断当前字母之前出现过没,若出现过则输出NaN结束当前循环,若没出现过接着下一个字母判断,直到结束。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
string a;
cin>>a;
//st用来存储字母出现的次数,用来判断是否多次出现该字母。
int st[27],ji=0;
//把st中的所有值赋为0。
memset(st,0,sizeof(st));
//若字符串中只有一个字母则不符合题意。
if(a.size()==1)
{
cout<<"NaN"<<endl;
continue;
}
//把第一个字母先加进去
st[a[0]-'a']++;
for(int i=1;i<a.size();i++)
{
st[a[i]-'a']++;
//判断回文
for(int j=i,k=a.size()-1;j<a.size();j++,k--)
{
if(j>=k)
{
cout<<"HE"<<endl;
//若成功则标记一下,直接结束当前循环
ji=1;
break;
}
if(a[j]!=a[k])
{
break;
}
}
//成功直接跳出
if(ji==1)
break;
//重点:不能先判断是否重复,应该先判断是否为回文串
if(st[a[i]-'a']>1)
{
cout<<"NaN"<<endl;
break;
}
}
};
return 0;
}
Problem C. Toxel 与随机数生成器

思路:由题意可知错误的生成器每次都生成同样的字符串,只需要把输入最短的字符串截取下来,用find查找一下若后面再次出现,则该生成器是错误的,若后面没出现过则是正确的。
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s,b="";
cin>>s;
//截取字符串或者可以直接调用substr(pos, len)函数。
for(int i=0;i<500;i++)
b+=s[i];
//判断是否存在过
if(s.find(b,1001)==-1)
{
cout<<"Yes";
}else
{
cout<<"No";
}
return 0;
}
Problem F. Art for Last

题意:在序列中选k项,找到k项最小的两个差的绝对值和最大的两个差的绝对值的乘积的最小值。
思路:想要乘积的绝对值最小,肯定是找几个差值最小的区间,把这区间的最大值和最小值相乘,在比较这些区间的最小值即可
若是一个有序序列的区间,相邻元素的差值最小,在该区间的找出最大值即区间最后一个减去第一个;最小值(肯定是相邻元素的差值)则需要判断,若用双重循环判断可能超时(1e9*1e9=10亿乘10亿的数据量),就需要用滑动窗口来优化最差10亿的数据量。
通过滑动窗口得到每个区间的最小值,在用每个区间的最小值乘最大值依次比较取最小值。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,k;
cin>>n>>k;
//a为输入数据,b为相邻数据的差值,c为每个区间的最小值,q为手写队列
int a[n+10],b[n+10],c[n+10],q[n+10];
for(int i=1;i<=n;i++)cin>>a[i];
//把数据先排序
sort(a+1,a+n+1);
//算出每个相邻区间的差值
for(int i=1;i<=n-1;i++)b[i]=a[i+1]-a[i];
//滑动窗口(单调队列)
int h=1,t=0,cnt=1;
for(int i=1;i<=n-1;i++)
{
while(t>=h&&b[q[t]]>=b[i])t--;
q[++t]=i;
if(q[h]<i-k+2)h++;
if(i>=k-1)c[cnt++]=b[q[h]];
}
//重点:单个元素最大值可能为1e9,相乘可能爆int,应该开long long
long long res=1e18;
for(int i=1;i<=n-k+1;i++)
{
res=min(res,(long long)c[i]*(a[i+k-1]-a[i]));
}
cout<<res;
return 0;
}
Problem G. Toxel 与字符画

思路:大模拟题,用三维数组,第三维的下标可以用来表示当前的数字。
单个元素数据量可能是1e18,存储的时候应该用longlong,先把等号前面的打印出来,在判断等号后面的是否符合题意,在判断的时候可能出现1e18*1e18可以用__int128数据类型
__int128是128位,大概10^38范围(__int128是C20里的)。特别注意x=1时的特判,不然可能卡到1e18次循环即100亿亿次循环会超时。
#include <bits/stdc++.h>
using namespace std;
//打表
char big[10][10][9] = {
{
"........",
"........",
".0000000",
".0.....0",
".0.....0",
".0.....0",
".0.....0",
".0.....0",
".0000000",
"........",
},
{
"........",
"........",
".......1",
".......1",
".......1",
".......1",
".......1",
".......1",
".......1",
"........",
},
{
"........",
"........",
".2222222",
".......2",
".......2",
".2222222",
".2......",
".2......",
".2222222",
"........",
},
{
"........",
"........",
".3333333",
".......3",
".......3",
".3333333",
".......3",
".......3",
".3333333",
"........",
},
{
"........",
"........",
".4.....4",
".4.....4",
".4.....4",
".4444444",
".......4",
".......4",
".......4",
"........",
},
{
"........",
"........",
".5555555",
".5......",
".5......",
".5555555",
".......5",
".......5",
".5555555",
"........",
},
{
"........",
"........",
".6666666",
".6......",
".6......",
".6666666",
".6.....6",
".6.....6",
".6666666",
"........",
},
{
"........",
"........",
".7777777",
".......7",
".......7",
".......7",
".......7",
".......7",
".......7",
"........",
},
{
"........",
"........",
".8888888",
".8.....8",
".8.....8",
".8888888",
".8.....8",
".8.....8",
".8888888",
"........",
},
{
"........",
"........",
".9999999",
".9.....9",
".9.....9",
".9999999",
".......9",
".......9",
".9999999",
"........",
},
};
char sml[10][10][7] = {
{
"......",
".00000",
".0...0",
".0...0",
".0...0",
".00000",
"......",
"......",
"......",
"......",
},
{
"......",
".....1",
".....1",
".....1",
".....1",
".....1",
"......",
"......",
"......",
"......",
},
{
"......",
".22222",
".....2",
".22222",
".2....",
".22222",
"......",
"......",
"......",
"......",
},
{
"......",
".33333",
".....3",
".33333",
".....3",
".33333",
"......",
"......",
"......",
"......",
},
{
"......",
".4...4",
".4...4",
".44444",
".....4",
".....4",
"......",
"......",
"......",
"......",
},
{
"......",
".55555",
".5....",
".55555",
".....5",
".55555",
"......",
"......",
"......",
"......",
},
{
"......",
".66666",
".6....",
".66666",
".6...6",
".66666",
"......",
"......",
"......",
"......",
},
{
"......",
".77777",
".....7",
".....7",
".....7",
".....7",
"......",
"......",
"......",
"......",
},
{
"......",
".88888",
".8...8",
".88888",
".8...8",
".88888",
"......",
"......",
"......",
"......",
},
{
"......",
".99999",
".9...9",
".99999",
".....9",
".99999",
"......",
"......",
"......",
"......",
},
};
char inf[10][25] = {
"........................",
"........................",
".IIIIIII.N.....N.FFFFFFF",
"....I....NN....N.F......",
"....I....N.N...N.F......",
"....I....N..N..N.FFFFFFF",
"....I....N...N.N.F......",
"....I....N....NN.F......",
".IIIIIII.N.....N.F......",
"........................",
};
char den[10][9]{
"........",
"........",
"........",
"........",
".=======",
"........",
".=======",
"........",
"........",
"........",
};
int main()
{
int t;
cin>>t;
while(t--)
{
//用vector来存储每个数字的值
vector<int> a,b,c;
//定义一个字符串来存储结果
string m[50];
long long x,y;
scanf("%lld^{%lld}",&x,&y);
long long tx,ty;
tx=x,ty=y;
//把每一个数拆出来,注意拆除了是倒序
while(tx)
{
a.push_back(tx%10);
tx/=10;
};
while(ty)
{
b.push_back(ty%10);
ty/=10;
};
//把数组内的数反转一下
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
//打印
for(int i=0;i<10;i++)
{
//打印x
for(auto k:a)
m[i]+=big[k][i];
//打印y
for(auto k:b)
m[i]+=sml[k][i];
//打印等号
m[i]+=den[i];
}
__int128 ans=1;
//定义一个变量要来标记ans是否越界
int ji=0;
//重点:需要特判
if(x!=1)
{
for(long long i=1;i<=y;i++)
{
ans*=x;
if(ans>1e18)
{
ji=1;
break;
}
}
}
//若越界打印inf,不越界打印ans的值
if(ji)
{
for(int i=0;i<10;i++)
m[i]+=inf[i];
}else
{
while(ans)
{
c.push_back(ans%10);
ans/=10;
}
reverse(c.begin(),c.end());
for(int i=0;i<10;i++)
{
for(auto k:c)
m[i]+=big[k][i];
}
}
//打印最后一列.
for(int i=0;i<10;i++)m[i]+=".";
//输出结果
for(int i=0;i<10;i++)cout<<m[i]<<endl;
};
return 0;
}
Problem H. Travel Begins

题意:{ai}的和为n。给你一个n和k个ai,通过给的四舍五入函数算出最大值和最小值。
思路:可知只要是大于等于0.5的的就可以看作1,只要尽可能的把n拆成尽可能多的0.5就能得到最大值。小于0.5的可以看作0把n拆乘尽可能接近0.5但小于0.5的值即可。
求最大值:一个n可以拆成2n个0.5,若2n个0.5>k,即只能拆出k-1个0.5,剩下的数给k的最后一个值(因为k个ai的和要为n),若2n<=k就可以把n都拆成0.5,把0.5都变成1在求剩下数的整数部分和小数部分即可。
求最小值:把一个n拆成尽可能多的接近0.5的数,需要判断的是如果2n=k的话,要拿出一个0.5,把剩下的0.5减去的值加到这上面来。
#include <bits/stdc++.h>
using namespace std;
//判断小数是否大于0.5
int check(double x)
{
return x-int(x)>=0.5?1:0;
}
int main()
{
int t;
cin>>t;
while(t--)
{
//大于一个尽可能小的数使得0.5-t接近0.5
double t=0.000000000001;
int n,k,num,ma,mi;
cin>>n>>k;
//判断最多可以分出多少个0.5
if(2*n>k)
num=k-1;
else
num=2*n;
//num即为1的数量再加上剩下的整数和小数。
ma=num+int(n-0.5*num)+check(n-0.5*num);
//判断是否要提出一个0.5好让其他的0.5小于0.5
if(2*n==k)
num=2*n-1;
//小于0.5的值为0即num为0,只须计算剩下的整数和小数即可。
mi=int(n-(0.5-t)*num)+check(n-(0.5-t)*num);
cout<<mi<<" "<<ma<<endl;
}
return 0;
}

浙公网安备 33010602011771号