NOIP2015普及组复赛
整套题都出得不错,难度适中,层次分明
建议同学们在做题的时候还是先在草稿纸上分析,把关键算法的伪代码写出来,然后设计数据进行静态查错,没有问题后再到电脑上敲出代码。实际效率会高一些,也不容易出错,除非是你对这道题太熟悉了,不需要分析就可以过的可以例外(当然每个人还是根据自己的具体情况而定,只是建议尝试一下)。
第一题 金币
感觉这道题出得不错,普及组第一题就应该是这样的
简单模拟,有一个小判断就可以了
设计数据时要考虑边界,比如本题的1,10000,其他还应考虑能够手工验算的,比如7,5组数据验证一下应该就没问题了
#include<iostream>
using namespace std;
int main(){
int k,n,d,s;
n=d=s=0;
cin>>k;
do{
n++;
d+=n;
if(d<=k) s+=n*n;//根据题目描述进行累加
else s+=(k-(d-n))*n;//d>k说明最后一次不足n天,计算一下天数来乘n即可
}while(d<k);
cout<<s;
return 0;
}
第二题 扫雷游戏
穷举整个雷区每一个元素周边的地雷情况就可以了
用dx,dy这样的增量数组写起来比较简洁,这个看每个人自己的习惯了
设计数据可以考虑全*,全?,边界值n,m为1的情况,100的情况需要设计随机数据代码来生成,有时间的话也可以考虑
#include<iostream>
#define N 105
using namespace std;
char a[N][N];
int b[N][N],dx[]={-1,1,0,0,-1,-1,1,1},dy[]={0,0,-1,1,-1,1,-1,1};
int main(){
int i,j,k,n,m,x,y;
cin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
cin>>a[i][j];
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(a[i][j]!='*')
for(k=0;k<8;k++){
x=i+dx[k];
y=j+dy[k];
if(x>0&&x<=n&&y>0&&y<=m&&a[x][y]=='*')
b[i][j]++;
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++)
if(a[i][j]=='*')cout<<'*';
else cout<<b[i][j];
cout<<endl;
}
return 0;
}
第三题 求和
这道题就有一定难度了,需要通过仔细的分析来找出规律,比较费时间
给出的数据也比较明确,也在引导你一步一步去进行深入思考
20分的数据,n<=100,和百钱百鸡相似,可以三重循环穷举
40分的数据,n<=3000,三重循环超时,可以只穷举x和z,用双重循环拿到40分,题目中也可以看到x和z的奇偶性必须是一致的,所以还可以优化一点儿
本题涉及到的数据比较大,所以在读数的时候考虑用scanf或者比scanf更优化的读入数据的方法,没准儿会多过一个点
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 100005
#define M 10007
using namespace std;
int number[N],color[N];
int main(){
int i,n,m,x,z;
long long s=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",number+i);
for(i=1;i<=n;i++)
scanf("%d",color+i);
for(x=1;x<n-1;x++)
for(z=x+2;z<=n;z+=2)
if(color[x]==color[z])
s=(s+(x+z)*(number[x]+number[z]))%M;
printf("%lld",s);
return 0;
}
60分及100分的数据,n<=100000,双重循环超时,只有考虑O(n)或nlogn算法,
可以对某种颜色的奇数或偶数部分进行分析,找出其中规律
如果不能完全判定自己找出的规律完全正确,建议将40分的程序做个函数包,处理n<=3000的情况,同时可以作为验算找规律的代码正确性的依据。
用某种颜色在奇数或偶数情况下有4个数的情况下做分析,k个数也同理可证
x x1 x2 x3
color=1 有4个编号 x1 x2 x3 x4 ax1 ax2 ax3 ax4
(x1+x2)(ax1+ax2) (x1+x3)(ax1+ax3) (x1+x4)(ax1+ax4)
(x2+x3)(ax2+ax3) (x2+x4)(ax2+ax4)
(x3+x4)(ax3+ax4)
x1*ax1+x1*ax2+x1*ax1+x1*ax3+x1*ax1+x1*ax4
x1*ax1*(k-2) + x1*(ax1+ax2+ax3+ax4+axk)=x1*(ax1*(k-2)+(ax1+ax2+...+axk))
x2*(ax2*(k-2)+(ax1+ax2+...+axk)) ... xk*(axk*(k-2)+(ax1+ax2+...+axk))
k个数的时候就是 x1*(ax1*(k-2)+(k个格子上数的和))+...+ xk(axk*(k-2)+(k个格子上数的和))
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 100005
#define M 10007
using namespace std;
int a[N],c[N];
long long s[N][2],k[N][2],ans;
int main(){
int i,n,m;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",a+i);
for(i=1;i<=n;i++){
scanf("%d",c+i);
s[c[i]][i%2]++;//某种颜色下奇数、偶数的可以组成三元组的格子数量
k[c[i]][i%2]=(k[c[i]][i%2]+a[i])%M;//累加该种情况下的格子上的数字和
}
for(i=1;i<=n;i++)
if(s[c[i]][i%2]>1)
ans=(ans+i*(a[i]*(s[c[i]][i%2]-2)%M+k[c[i]][i%2]))%M;//列举计算结果
printf("%lld",ans);
return 0;
}
求模运算容易出错
需要设计结果超出10007的
https://blog.csdn.net/deaidai/article/details/76474018?utm_source=blogxgwz2
第四题 推销员
算法众多,数学强的可以写出好简洁的贪心算法,佩服牛人
NOIP2014 普及组复赛
1. 珠心算测验(count.cpp/c/pas)
【问题描述】
珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练,既能够开发智力,又能够为日常生活带来很多便利,因而在很多学校得到普及。
某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他随机生成一个正整数集合,集合中的数各不相同,然后要求学生回答:
其中有多少个数,恰好等于集合中另外两个(不同的)数之和?
最近老师出了一些测验题,请你帮忙求出答案。
【输入】
输入文件名为 count.in。
输入共两行,第一行包含一个整数 n,表示测试题中给出的正整数个数。
第二行有 n 个正整数,每两个正整数之间用一个空格隔开,表示测试题中给出的正整数。
【输出】
输出文件名为 count.out。
输出共一行,包含一个整数,表示测验题答案。
【输入输出样例】
count.in |
count.out |
4 1 2 3 4 |
2 |
【样例说明】
由 1+2=3,1+3=4,故满足测试要求的答案为 2。注意,加数和被加数必须是集合中的两个不同的数。
【数据说明】
对于 100%的数据,3 ≤ n ≤ 100,测验题给出的正整数大小不超过 10,000。
简单解析:
这道题出错的同学不少,关键就是没有仔细看题目描述中的红色部分,理解出错
枚举:C(n,2),n个数的两两不同数的组合情况
int n,i,j,k,a[105],b[105],s=0;
memset(b,0,sizeof(b));
cin>>n;
for(i=1;i<=n;i++)cin>>a[i];
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
for(k=1;k<=n;k++)
if(a[i]+a[j]==a[k]) b[k]=1;//错误代码:if(a[i]+a[j]==a[k]) s++;
for(i=1;i<=n;i++)
s+=b[i];
cout<<s;
如果变成双重循环的话
int n,i,j,k,a[105],b[20005],s=0;//此处有同学设为b[105],造成下标越界
memset(b,0,sizeof(b));
cin>>n;
for(i=1;i<=n;i++)cin>>a[i];
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
b[a[i]+a[j]]=1;
for(i=1;i<=20000;i++)
s+=b[i];
cout<<s;
2.比例简化(ratio.cpp/c/pas)
【问题描述】在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果。例如,对某一观点表示支持的有 1498 人,反对的有 902 人,那么赞同与反对的比例可以简单的记为
1498:902。
不过,如果把调查结果就以这种方式呈现出来,大多数人肯定不会满意。因为这个比例的数值太大,难以一眼看出它们的关系。对于上面这个例子,如果把比例记为 5:3,虽然与真实结果有一定的误差,但依然能够较为准确地反映调查结果,同时也显得比较直观。
现给出支持人数 A,反对人数 B,以及一个上限 L,请你将 A 比 B 化简为 A’ 比 B’,要求在 A’和 B’ 均不大于 L 且 A’ 和 B’ 互质(两个整数的最大公约数是 1)的前提下,
A’ / B’ ≥ A / B且 A’ / B’ – A / B 的值尽可能小。
【输入】输入文件名为 ratio.in。
输入共一行,包含三个整数 A,B,L,每两个整数之间用一个空格隔开,分别表示支持人数、反对人数以及上限。
【输出】输出文件名为 ratio.out。
输出共一行,包含两个整数 A’,B’,中间用一个空格隔开,表示化简后的比例。
【输入输出样例】
ratio.in |
ratio.out |
1498 902 10 |
5 3 |
【数据说明】
对于 100%的数据,1 ≤ A ≤ 1,000,000,1 ≤ B ≤ 1,000,000,1 ≤ L ≤ 100,A/B ≤ L。
int Gcd(int a,int b){
if(b==0) return a;
return Gcd(b,a%b);
}
int main(){
int A,B,L,A1,B1,t,i,j;
double f,f1,m;
cin>>A>>B>>L;
m=L;
f=double(A)/B;
for(i=1;i<=L;i++)
for(j=1;j<=L;j++){
f1=double(i)/j;
if( f1>=f && f1-f<m && Gcd(i,j)==1 ){ //也可以直接写__Gcd(i,j)==1
m=f1-f;
A1=i;
B1=j;
}
}
cout<<A1<<' '<<B1;
retur