8.18集训笔记
上午递归,文件
点击查看代码
#include<bits/stdc++.h>
using namespace std;
//#define T long long
typedef long long LL; // 取别名,以后使用 LL 就是 long long
const int N=5e3+10;
LL fib[N];
LL f(int n){ // 递归
if(n<=2) return 1;
return f(n-1) + f(n-2);
}
int main(){
fib[0]=fib[1]=1; // 递推
for(int i=2; i<N; i++) {
fib[i] = fib[i-1] + fib[i-2];
// cout<<i<<" : "<< fib[i]<<endl;
}
int n;cin>>n;
// cout<<f(n)<<endl;
cout<<fib[n]<<endl;
return 0;
}
- 汉诺塔
【例】Hanoi(汉诺)塔问题。
古代有一个梵塔,塔内有3个座A,B,C。开始时A座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个老和尚想把这64个盘子从A座移到C座,但规定每次只允许移动一个盘,且在移动过程中在3个座上都始终保持大盘在下,小盘在上。在移动过程中可以利用B座。要求编程序输出移动盘子的步骤。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=110;
void hanoi(int n,char a,char b,char c){
if(n==1){
// printf("%c->%c\n",a,c);
cout<<a<<" "<<c<<endl;
return;
}
hanoi(n-1, a,c,b);
hanoi(1, a,b,c);
hanoi(n-1, b,a,c);
}
int main(){
int n; cin>>n; // scanf("%d", &n);
hanoi(n,'A','B','C');
return 0;
}
点击查看代码
#include<iostream>
using namespace std;
const int N=1e6+10, INF=0x3f3f3f3f;
typedef long long LL;
int p;
LL halfpow(int a,int n){
if(n==1) return a;
LL ans = halfpow(a, n/2);
ans = ans*ans%p;
if(n&1) ans=ans*a%p;
return ans;
}
int main(){
int a,b; scanf("%d%d%d",&a,&b,&p);
LL s = halfpow(a, b);
printf("%d^%d mod %d=%lld\n",a,b,p,s);
return 0;
}
文件重定向
这个其实主要对于数据测试和竞赛中有用,平常用不上,但是比赛又必须要使用
#include<iostream>
#include<cstdio>
using namespace std;
int main() {
//替代我们的手动输入,将文件中内容读入输入流中
freopen("输入文件名", "r", stdin);
//将输出流中的数据写入文件中
freopen("输出文件名", "w", stdout);
//正常的执行程序
long long a,b; cin>>a>>b;
cout<<a+b<<endl;
fclose(stdin); //关闭输入流
fclose(stdout); //关闭输出流
return 0;
}
在 windows 系统上可以不写fclose,系统会自动关闭,
但是 Linux 系统上如果不写会出现问题,无法正常关闭或文件保存出现问题,OI系列评测均是在Linux系统上进行的,所以一定要手动关闭。
其实用这个来代替我们的手动输入是比较方便的,大家可以多习惯这样的用法。
举例:【题目描述】输入a,b, 输出a+b的结果(\(0<=a, b<=2^31\))。
| 题目 | a+b |
|---|---|
| 提交源文件名 | ab.cpp |
| 输入文件名 | ab.in |
| 输出文件名 | ab.out |
最后应该提交的的源文件,应当命名为:ab.cpp(注意大小写),并存放如下内容
#include<iostream>
#include<cstdio>
using namespace std;
int main() {
freopen("ab.in", "r", stdin); //从文件 ab.in 中读入信息
freopen("ab.out", "w", stdout); //将输出信息保存至文件 ab.out
long long a,b; cin>>a>>b;
cout<<a+b;
fclose(stdin); //关闭输入流
fclose(stdout); //关闭输出流
return 0;
}
这样程序就会从文件 ab.in 里面读取前两个数据,分别赋值给a,b,从而替代我们手动输入的过程。
并且会将a+b的结果保存到文件 ab.out 里面,就不会在控制台窗口输出(也就是小黑方框)。
另外注意,OI(CSP/NOIP/..)系列的比赛中源文件提交有格式要求,注意官网信息,这里大概说明几点:
- 源程序命名,严格按照题目说明,Linux区分大小写,Windows不区分大小写;
- 源程序存放位置(D:\noip\)不一定;
部分省份会在测评前用脚本处理源文件存放格式,
删除命名不对/多余/存放路径不对/..的文件,一旦错了就没了。
这里主要一点就是:如果考前通告中说明,不需要按照试题存放源文件,
而是直接(D:\noip\),那么就需要按照官网通知来,注意官网信息。
如果确实不清楚,考场询问监考老师,或者两种方式都复制一份保险。
下午测试讲评
开心小测
请大家在桌面新建文件夹,用自己中文名命名,将源程序(英文名.cpp)保存至该文件夹。
- 文件输入输出,1S,512MB
身份证号码/idcard
题目描述
身份证号码是由十七位数字本体码和一位校验码组成。排列顺序从左到右依次为:六位数字“地址码”、八位数字“出生日期码”、三位数字“顺序码”和一位数字“校验码”。
“地址码”用来表示公民常住户口所在地区的行政区划代码;“出生日期码”表示公民的出生年月日;“顺序码”表示在同一“地址码”所表示的区域范围内,对同年同月同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。“校验码”是根据前面十七位数字计算得到,计算方法为:
第 \(1\) 步:将身份证号码乘以不同的系数,第 \(1\sim 17\) 位的系数分别为 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2;
第 \(2\) 步:将这 \(17\) 位数字和系数相乘的结果相加;
第 \(3\) 步:用加出来的和除以 \(11\),得到余数;
第 \(4\) 步:余数只可能有 \(0\sim10\) 这 \(11\) 个数字,其分别对应的校验码为 1, 0, X, 9、8, 7, 6, 5, 4, 3, 2;也就是说如果上面得到的余数为 \(2\),那校验码就是 \(X\),如果余数为 \(10\),那校验码就是 \(2\)。
现在你只记得自己身份证上的前 \(17\) 位,你能否不用回家拿身份证就可以知道最后一位是多少?
输入格式
输入只有一行,由 \(17\) 个数字组成,表示身份证号码的前 \(17\) 位,数字和数字之间用空格隔开。
输出格式
输出该身份证的最后一位校验码。
| 输入样例 | 输出样例 |
|---|---|
4 4 2 0 0 0 1 9 9 6 0 1 0 1 0 2 3 |
4 |
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m,a[N];
int b[]={7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
int c[]={ 1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2};
int main(){
freopen("idcard.in", "r", stdin);
freopen("idcard.out", "w", stdout);
n = 17;
for(int i=0; i<n; i++) cin>>a[i];
int x=0;
for(int i=0; i<n; i++) x += a[i] * b[i];
x %= 11;
if(x==2) cout<<char(c[x]);
else cout<<c[x];
fclose(stdin); fclose(stdout);
return 0;
}
数位和/sum
对于一个整数,我们定义它的数位和为各个数位的和。
比如 \(456789\) 的数位和为 \(4+5+6+7+8+9\) 也就是 \(39\)。
我们对这个 \(a\) 不停地求数位和,直到它不超过 \(9\),把所有的中间结果输出。
比如 \(a=456789\),那么它的数位和是 \(39\)。继续对 \(39\) 求数位和,得到 \(12\),继续对 \(12\) 操作,得到 \(3\)。
输入格式
一个正整数 \(a\)
输出格式
若干行,为 \(a\) 不停地求数位和,直到不超过 \(9\) 为止的所有结果
| 输入样例 | 输出样例 |
|---|---|
456789 |
39 12 3 |
86471648912748124790174817001304807 |
149 14 5 |
数据范围
\(50\%\)的数据,\(1\leq a\leq 10^9\)
\(80\%\)的数据,\(1\leq a\leq 10^{18}\)
\(100\%\)的数据,\(1\leq a\leq 10^{1000}\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,a[N];
char s[N];
int main(){
freopen("sum.in", "r", stdin);
freopen("sum.out", "w", stdout);
cin>>s; n = strlen(s);
for(int i=0; i<n; i++) m += s[i]-'0';
cout<<m<<endl;
while(m>9){
int t=m, a=0;
while(t) a += t%10, t/=10;
m = a;
cout<<m<<endl;
}
fclose(stdin); fclose(stdout);
return 0;
}
晨跑/running
题目描述
每天锻炼一小时,健康工作五十年,幸福生活一辈子。
在清华,体育运动绝对是同学们生活中不可或缺的一部分。为了响应学校的号召,模范好学生王队长决定坚持晨跑。不过由于种种原因,每天都早起去跑步不太现实,所以王队长决定每 \(a\) 天晨跑一次。换句话说,假如王队长某天早起去跑了步,之后他会休息 \(a-1\)天,然后第\(a\)天继续去晨跑,并以此类推。
王队长的好朋友小钦和小针深受王队长坚持锻炼的鼓舞,并决定自己也要坚持晨跑。为了适宜自己的情况,小钦决定每 \(b\) 天早起跑步一次,而小针决定每 \(c\) 天早起跑步一次。
某天早晨,王队长、小钦和小针在早起跑步时相遇了,他们非常激动、相互鼓励,共同完成了一次完美的晨跑。为了表述方便,我们把三位同学相遇的这天记为第 \(0\) 天。假设三位同学每次晨跑的时间段和路线都相同,他们想知道,下一次三人在跑步时相遇是第几天。由于三位同学都不会算,所以希望由聪明的你来告诉他们答案。
输入格式
输入共一行,包含三个正整数 \(a,b,c\),表示王队长每隔 \(a\) 天晨跑一次、小钦每隔 \(b\) 天晨跑一次且小针每隔 \(c\) 天晨跑一次。
输出格式
输出共一行,包含一个正整数 \(x\),表示三位同学下次将在第 \(x\) 天相遇。
| 输入样例 | 输出样例 |
|---|---|
2 3 5 |
30 |
3 4 6 |
12 |
10 100 1000 |
1000 |
数据范围
\(\#1-5: 1≤a,b,c≤50\)
\(\#6-7: 1≤a,b,c≤1000\)
\(\#8-10: 1≤a,b,c≤10^5\)
分析:一句话题意,求三个数的最小公倍数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1010;
LL gcd(LL a,LL b){
return b?gcd(b,a%b) : a;
}
LL lcm(LL a,LL b){
return a/gcd(a,b) * b;
}
int main(){
freopen("running.in", "r", stdin);
freopen("running.out", "w", stdout);
LL a,b,c; cin>>a>>b>>c;
cout<<lcm(lcm(a,b), c);
fclose(stdin); fclose(stdout);
return 0;
}
孤独的素数/prime
题目描述
在一个 \(n\) 行 \(m\) 列的矩阵王国中,生活着一些整数,其中一些是素数,一些不是素数。
如果一个素数的上下左右、左上、右上、左下、右下相邻的数中都没有素数,我们就认为这是一个孤独的素数。
比如:一个 \(3\) 行 \(5\) 列的矩阵如下。
3 8 10 9 5
6 10 2 4 13
8 8 9 6 3
这个矩阵中有 \(2\) 个素数,分别是第 \(1\) 行第 \(1\) 列的 \(3\),和第 \(2\) 行第 \(3\) 列的 \(2\)。
请编程计算出,一个 \(n\) 行 \(m\) 列的矩阵中有多少个孤独的素数?
输入格式
第 \(1\) 行有 \(2\) 个整数 \(n\) 和 \(m\),代表矩阵的大小
接下来 \(n\) 行,每行有 \(m\) 个整数
输出格式
输出 \(1\) 个整数,代表矩阵中孤独素数的个数。
| 输入样例 | 输出样例 |
|---|---|
3 5 3 8 10 9 5 6 10 2 4 13 8 8 9 6 3 |
2 |
数据范围
对于 \(100\%\) 的数据,\(3≤n,m≤50\),矩阵中的元素是\(1\)~\(1000\) 之间的整数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=55;
int n,m,a[N][N],ans;
int dx[]={-1,1,0,0,-1,-1,1,1};
int dy[]={0,0,-1,1,-1,1,-1,1};
bool isp(int n){
// [2, n-1] ----> [2, sqrt(n)] ---> i<=n/i
for(int i=2; i<=n/i; i++)
if(n%i==0) return 0;
return n > 1;
}
int main(){
freopen("prime.in", "r", stdin);
freopen("prime.out", "w", stdout);
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++) cin>>a[i][j];
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++){
bool f=1;
for(int k=0; k<8; k++){
int tx = i+dx[k], ty=j+dy[k];
if(tx>=1 && tx<=n &&ty>=1 &&ty <=m && isp(a[tx][ty])){
f=0; break;
}
}
if(f && isp(a[i][j])) ans++;
}
cout<<ans;
fclose(stdin); fclose(stdout);
return 0;
}
博物馆/museum
题目描述
从前,有一个偌大的博物馆,每天都会有数以万计的人们来参观,欣赏这里的艺术作品。这一天,博物馆来了 \(n\) 批人,第 \(i\) 批人有 \(A_i\) 个人以及一个导游组成,他们依次到达,但同时也有一些批次的人离开,由于人次太多,博物馆的管理人员递给你一些人数表,就请你来统计一下剩下多少人。
输入格式
第一行是个整数 \(n\),接下来 \(n\) 行,每行两个数,第一个数 \(X\),如果 \(X=0\) 则后面接一个数 \(A_i\),表示来了 \(A_i\) 个人;如果 \(X=1\),那么接下来就有一个数 \(Y\),表示来的人中的第 \(Y\) 批离开了。
输出格式
一个数,表示剩下多少人。
| 输入样例 | 输出样例 |
|---|---|
6 0 5 0 6 1 1 0 7 0 8 1 3 |
16 |
样例解释:有四批人,每批人要加上一位导游,分别是 \(6,7,8,9\) 人,离开的是第 \(1\) 和 \(3\) 批,即走了 \(6+8=14\) 人,剩 \(7+9=16\) 人。
数据范围
\(\#1-3: 1≤n≤100,1≤A_i≤1000\);
\(\#4-10: 1≤n≤10^6,1≤A_i≤10^6\)。
保证:\(X\) 只为 \(0\) 或 \(1\),\(Y\) 一定符合要求。
分析:读清题目,使用 st[p] 表示 第 p 批人的数量。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10;
int n,m,x,y,p,st[N];
int main(){
freopen("museum.in", "r", stdin);
freopen("museum.out", "w", stdout);
cin>>n;
for(int i=1; i<=n; i++){
cin>>x>>y;
if(x==0) st[++p] = y+1;
else if(x==1) st[y] = 0;
}
LL ans=0;
for(int i=1; i<=p; i++) ans += st[i];
cout<<ans;
fclose(stdin); fclose(stdout);
return 0;
}

浙公网安备 33010602011771号