第一周题单均为入门训练,难度较低,考察程序的结构设计及数组和字符串,部分题目可使用C++中STL提高解题效率。
目录
P1217 [USACO1.5] 回文质数 Prime Palindromes
顺序结构
P1001 A+B Problem
P1001 A+B Problemhttps://www.luogu.com.cn/problem/P1001
算法:模拟
思路:按原题题意,读入a和b,输出a+b的结果即可。
直接解法
//输入1——C++特性,流输入输出
int main() {
int a,b;
cin >> a >> b;
cout << a+b;
return 0;
}
//输入2
int main(){
int a,b;
scanf("%d%d",&a,&b);
printf("%d",a+b);
return 0;
}
//定义一个变量记录a+b的结果
int a,b;
int c;
int main(){
scanf("%d%d",&a,&b);
c=a+b;
printf("%d",c);
return 0;
}
P3954 [NOIP2017 普及组] 成绩
P3954 [NOIP2017 普及组] 成绩https://www.luogu.com.cn/problem/P3954
算法:模拟
思路:按原题题意
直接解法
int a,b,c,sum;
int main(){
//输入三个成绩
scanf("%d%d%d",&a,&b,&c);
//总成绩=作业成绩×20%+×20%+小测成绩×30%+×30%+期末考试成绩×50%×50%
sum=a*0.2+b*0.3+c*0.5;
//输出结果
printf("%d",sum);
return 0;
}
对数据说明的分析
关于“对于 30%的数据,A=B=0
对于另外 30% 的数据,A=B=100
对于 100%100% 的数据,0≤A,B,C≤100”
本题数据总量不大,对于大数据量(或者数据复杂)的题目,可以根据这种数据范围的提示分类讨论
关于“A,B,C 都是 10 的整数倍” :也就是A,B,C乘上百分数后得到的一定是整数,不存在小数,那么最后的总成绩也就是整数。那么这里的总成绩变量可以使用int类。
P1421 小玉买文具
P1421 小玉买文具https://www.luogu.com.cn/problem/P1421#submit
C语言和C++都不支持小数对小数的除法。所以对于这道题,我们可以考虑将小玉持有的钱单位从“元”和“角”转换为“角”,同样的将铅笔的单价转换为19角,这样二者都变成了整数。我们进行整数除法就能求出答案。同时,“/”运算符是取商的,符合我们对结果的要求(去尾法)。
int main(){
int a,b,c;
cin>>a>>b;
c=a*10+b;
cout<<c/19;
return 0;
}
P1075 [NOIP2012 普及组] 质因数分解
P1075 [NOIP2012 普及组] 质因数分解https://www.luogu.com.cn/problem/P1075
由题意可知,输入的数共4个因数:1和它本身;另外两个不同质数
为了节约计算成本,我们先求出除了1之外的较小的因数,再用除法求出较大因数。
sqrt()是开平方函数,对应C语言库math.h,C++库为cmath。
一个数除了它本身的最大因数,小于等于这个数的开平方。
int n,a,b;
int main(){
cin>>n;
for(a=2;a<=sqrt(n);a++){
if(n%a==0)
b=n/a;
}
cout<<b;
return 0;
}
分支结构
P3954 [NOIP2017 普及组] 成绩
P3954 [NOIP2017 普及组] 成绩https://www.luogu.com.cn/problem/P3954
算法:模拟
思路:按原题题意,同时使用if作条件判断
int a,b,c,sum;
int main(){
//输入三个成绩
scanf("%d%d%d",&a,&b,&c);
//A=B=0
if(a==0&&b==0)
sum=0.5*c;
//A=B=100
else if(a==100&&b==100)
sum=20+30+0.5*c;
else sum=sum=a*0.2+b*0.3+c*0.5;
printf("%d",sum);
return 0;
}
循环结构
P1085 [NOIP2004 普及组] 不高兴的津津
P1085 [NOIP2004 普及组] 不高兴的津津https://www.luogu.com.cn/problem/P1085
思路:模拟
使用了for循环来记录按天输入的数据(每天的时长)
int a,b,i,v,m=0;
//一周七天,i表示周几,a表示上课时间,b表示复习班时间
for(i=1;i<=7;i++)
{scanf("%d%d",&a,&b);
//m记录最大值。
//将m记录为0是因为每天的时长之和一定大于0,所以将m记录为一个比它小的数字才能保证出现符合要求的数时m可以执行它的记录功能。
//两个判断没有等于号:分别对应输出格式的“靠前一天”(也就是两天程度相当时仍然保持记录的是靠前面的一天)和要求中的“超过”八小时
if(m<(a+b)&&(a+b)>8){
//m记录时长总和,v记录星期几
m=a+b;v=i;
}
}
printf("%d",v);
return 0;
}
P1909 [NOIP2016 普及组] 买铅笔
P1909 [NOIP2016 普及组] 买铅笔https://www.luogu.com.cn/problem/P1909
思路:按题意,使用for输入并分类讨论,在讨论过程中使用if判断
int main(){
//ans为0与上一题相同,都是“将不可能得到的数字0赋给记录的变量,用这个变量记录正数”
int n,ans=0,x,y,m,w;
cin>>n;
for(int i=1;i<=3;i++){
cin>>x>>y;
//求n%x余数是否为0.为0则买整包可以刚好满足发礼物的要求,否则要多买一包才能够发礼物(且有剩余)
if(n%x!=0)
m=n/x+1;
else m=n/x;
//w记录得到的包数乘以每包的铅笔数量,如果w比
w=m*y;
//判断条件1(二者满足其一就进行记录):w比ans值更小,也就是对于这款铅笔,购买可以更省钱,就更新ans为w的值
if(w<ans||ans==0)
//条件2:如果花费记录为0(也就是连一个购买方案都没有)就先记录它
ans=w;
}
cout<<ans;
return 0;
}
数组
P1009 [NOIP1998 普及组] 阶乘之和
P1009 [NOIP1998 普及组] 阶乘之和https://www.luogu.com.cn/problem/P1009
使用高精度求阶乘的和。
using namespace std;
int n,a[90],b[90],c[90],f[90],d=0,len_a,len_b=1,len_c=1,len_ans,m=1;
int main(){
cin>>n;
b[0]=1;
for(int i=1;i<=n;i++){
len_a=0;
int p=i;
while(p>0){
a[len_a++]=p%10;
p/=10;
}
for(int j=0;j<len_a;j++)
for(int k=0;k<=len_b;k++)
c[j+k]+=a[j]*b[k];
for(int j=0;j<len_c;j++)
if(c[j]>9) c[j+1]+=c[j]/10,c[j]%=10;
if(c[len_c]) len_c++;
len_ans=len_b,len_b=len_c,m=max(m,len_c);
for(int k=len_c-1;k>=0;k--) b[k]=c[k];
len_c=len_a+len_ans;
memset(c,0,sizeof(c));
for(int j=0;j<m;j++){
f[j]+=b[j];
if(f[j]>9) f[j+1]+=f[j]/10,f[j]%=10;
}
}
while(!f[m]&&m>0) m--;
for(int i=m;i>=0;i--) cout<<f[i];
return 0;
}
P1427 小鱼的数字游戏
P1427 小鱼的数字游戏https://www.luogu.com.cn/problem/P1427
思路:用数组记录。读入一串数字,(遇到0时停止),再倒过来输出。
常规做法:使用数组
int x[100],len=0;
int main(){
for(int i=0;;i++){
cin>>x[i];
if(x[i]==0)
break;
len=i;
}
for(int j=len;j>=0;j--)
cout<<x[j]<<" ";
return 0;
}
(模拟实现)栈
利用了栈先进后出的特性。
int a[105];
//top记录顶部元素在数组中的下标,c表示每次读入的数字
int top=0,c;
int main(){
while(1){
cin>>c;
if(c==0) break;
a[++top]=c;
/*上一行代码等价于下列两行代码
top++;
a[top]=c;
*/
}
while(top!=0){
cout<<a[top--]<<" ";
/*
上一行代码等价于下列两行代码:
cout<<a[top];
top--;
*/
}
return 0;
}
(C++ STL)栈
stack<int>a;
int c;
int main(){
while(cin>>c){
if(c==0) break;
a.push(c);
}
while(!a.empty()){
cout<<a.top()<<' ';//输出顶部元素
a.pop() ;//弹出顶部元素
}
return 0;
}
P2141 [NOIP2014 普及组] 珠心算测验P2141 [NOIP2014 普及组] 珠心算测验https://www.luogu.com.cn/problem/P2141
题意:输入一组数字,判断其中哪些数可以又数组中的另外两个数字求和得到。
注意:加数+加数=和,非负数之和一定大于两个加数。
思路:先对数组排序。在使用三层循环,结合判断。
其中sort()为C++的STL库中排序函数,默认升序排序。C语言必须手写排序。
//n代表数字总数,a记录输入的数字,ans为答案,qc按下标标记数字是否可以由求和得到
int n,a[105],ans,qc[105];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<n-1;i++)//加数
for(int j=i+1;j<n;j++)
for(int k=j+1;k<=n;k++){//被加数
//加数+加数=和。如果得到的和在数组中,和对应的qc还没有标记,就标记一下为1。
//qc[k]=0的初始化可以防止重复标记
if((a[i]+a[j]==a[k])&&qc[k]==0){
ans++;
qc[k]=1;
}
}
cout<<ans;
return 0;
}
字符串
P5015 [NOIP2018 普及组] 标题统计
P5015 [NOIP2018 普及组] 标题统计https://www.luogu.com.cn/problem/P5015
题意:读入一个字符串,记录其中的字符数量,不记录空格和换行符。
思路:逐个读入字符串中的字符,不是空格和换行符,那么记录变量+1。
C语言没有单独的字符串类,我们用字符数组来记录字符串。可以使用gets()函数实现字符数组的读入。同时,使用strlen()函数来求字符数组(也就是字符串)的长度。
int main(){
char s[10];
gets(s);
int n=strlen(s);
int ans=0;
for(int i=0;i<n;i++){
//如果是大写字母
if(s[i]>='A'&&s[i]<='Z')
ans++;
//如果是小写字母
if(s[i]>='a'&&s[i]<='z')
ans++;
//如果是数字字符
if(s[i]>='0'&&s[i]<='9')
ans++;
}
printf("%d",ans);
return 0;
}
C++:cin可以自动过滤掉换行符不做处理。
char s;
int ans;
int main(){
while(cin>>s){
if(s!=' ')
ans++;
}
cout<<ans;
return 0;
}
P1321 单词覆盖还原
C++对字符串类的处理函数使用cstring库
//字符串;记录boy数量和girl数量的变量
string s;
int kb,kg;
//处理函数,在字符串s中找字符串t
int cs(string t){
int k=0;
//求出t字符串总长度,fi记录是字符串s能否找的到t
//find(t,k)是在k下标后找串t,找到返回下标,找不到返回-1。不特意标注k则从头开始。
int len=t.size(),fi=s.find(t);
while(fi!=-1){//继续查找的条件是能找到
k++;
//找到之后用*覆盖掉串t,防止重复计算。
s.replace(fi,len,"*");
fi=s.find(t,fi+1);
}
return k;
}
//查找时,boy和girl组成的字母都不重复,分开查找就行,不用分类讨论;分别对二者查找时,先从长串(最长的串就是单词本身)寻找和覆盖,在向短的串寻找,可以防止漏数。
int main(){
cin>>s;
kb=cs("boy")+cs("bo")+cs("oy")+cs("b")+cs("o")+cs("y");
kg=cs("girl")+cs("gir")+cs("irl")+cs("gi")+cs("ir")+cs("rl")+cs("g")+cs("r")+cs("i")+cs("l");
cout<<kb<<endl<<kg<<endl;
return 0;
}
P1217 [USACO1.5] 回文质数 Prime Palindromes
P1217 [USACO1.5] 回文质数 Prime Palindromeshttps://www.luogu.com.cn/problem/P1217
常规思路:先求出一亿之内所有质数,在判断是不是回文数。
解法1:埃氏筛选法+回文数判断
using namespace std;
//设置布尔型数组,数组元素记录下标数字是否为质数,是则为1
//注意一亿是8个0,共9位
bool book[100000001];
// 用埃氏筛法生成质数表
void isprime(int b) {
//初始化,默认全是质数
memset(book, true, sizeof(book));
book[1]=false; //1不是质数
int n=sqrt(b);//用于判断。一个数的除了它自身的最大因数不大于它的开平方
for (int i=2;i<=n;i++) {
if (book[i]) {
//质数的倍数不是质数
//b/i用于限制,防止j*i>=b时浪费
for (int j=2;j<=b/i;j++)
book[i*j]=false;
}
}
}
//判断回文数
bool is(int num) {
//temp用来求每一位
//num是原来的数字,ans用来求原数字数字顺序反过来得到的数,二者相等则num为回文数
int temp=num,ans=0;
while (temp!=0) {
ans=ans*10+temp%10;
temp/=10;
}
if (ans==num)
return true;
else
return false;
}
int main() {
int a,b;
cin>>a>>b;
/*可以证明的是,除了11以外,一个数的位数是偶数的话,不可能为回文数素数。
如果一个回文素数的位数是偶数,则它的奇数位上的数字和与偶数位上的数字和必然相等;
根据数的整除性理论,容易判断这样的数肯定能被11整除,所以它就不可能是素数。
也就是说,一亿一一定不是,接下来的八位数一定都不是。所以,如果b是八位数或九位数,
为了节省时空,我们从最大七位数开始算。
*/
if (b>=10000000)
b=9999999;
isprime(b);
//a>b时这个输入范围不合法,不予计算
if(a>b)
return 0;
//a是偶数那么范围再向前一下,因为a本身一定不是。
if (a%2==0)
a++;
//除了2以外,2的倍数不可能是质数
for (int i=a;i<=b;i+=2) {
if (book[i] && is(i))
cout<<i<<endl;//如果既是质数同时也是回文数,就输出。
}
return 0;
}
解法2:使用打表得到一个记录了1亿内质数的数组(推荐使用python),再判断。