目录
杨辉三角
Fibonacci数列
The Biggest Water Problem
数字根:
Digit Sum of N!
模的运算性质:
[NOIP2005]谁拿了最多奖学金
牛牛的汉诺塔
结尾
杨辉三角
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
杨辉三角形又称Pascal三角形,它的第i+1行是(a+b)(i)的展开式的系数。
它的一个重要性质是:三角形中的每个数字等于它两肩上的数字相加。
下面给出了杨辉三角形的前4行:
1
1 1
1 2 1
1 3 3 1
给出n,输出它的前n行。
输入描述:
输入包含一个数n。
输出描述:
输出杨辉三角形的前n行。每一行从这一行的第一个数开始依次输出,中间使用一个空格分隔。请不要在前面和后面输出多余的空格。
示例1
输入
复制4
4
输出
复制1 1 1 1 2 1 1 3 3 1
1
1 1
1 2 1
1 3 3 1
说明
1 <= n <= 34。
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
// 使用一个二维向量来存储杨辉三角的每一行
vector<vector<int>> a(n);
// 生成杨辉三角
for (int i = 0; i < n; ++i) {
// 每一行的第一个和最后一个数字都是1
a[i].resize(i + 1, 1);
// 从第三行开始,中间的数字通过上一行的两个数字相加得到
for (int j = 1; j < i; ++j) {
a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
}
}
// 输出杨辉三角
for (int i = 0; i < n; ++i) {
for (int j = 0; j <= i; ++j) {
cout << a[i][j];
if (j < i) {
cout << " ";
}
}
cout << endl;
}
return 0;
}
这段代码巧妙的地方在于,并没有直接开辟n*n
空间大小的vector
类型的空间。而是先开辟n
行的数据,声明这些数据的数据类型是vector<int>
类型,但是不给每一行开辟空间。因为每一行都对应一个空间大小。第一行对应一个元素,第二行对应两个元素,以此类推。所以对于每一行的列信息都是不同的,因此我们选择在遍历每一行的时候再去开辟对应列信息的空间大小。使用resize
语句。
接着对于每一行第二个元素到 倒数第二个元素用两肩之和计算得到数据。
最后输出杨辉三角,控制最后一次输出后面不添加""
。每输出一行就换行操作。
Fibonacci数列
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
Fibonacci数列的递推公式为:F(n)=F(n-1)+F(n-2),其中F(1)=F(2)=1。 当n比较大时,F(n)也非常大,现在我们想知道,F(n)除以10007的余数是多少。
输入描述:
输入包含一个整数n。
输出描述:
输出一行,包含一个整数,表示F(n)除以10007的余数。
示例1
输入
复制10
10
输出
复制55
55
示例2
输入
复制22
22
输出
复制7704
7704
备注:
说明:在本题中,答案是要求F(n)除以10007的余数,因此我们只要能算出这个余数即可,而不需要先计算出F(n)的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数再取余简单。
数据规模与约定
1 <= n <= 1,000,000。
#include <bits/stdc++.h>
using namespace std;
using LL=long long;
LL N=1000005;
LL MOD=10007;
int main(){
vector<LL>a(N);
a[1]=1;
a[2]=1;
for(int i=3;i<N;i++){
a[i]=(a[i-1]+a[i-2])%MOD;
}
int n;
cin>>n;
cout<<a[n];
}
动态规划的思想,定义a[i]
表示斐波那契数列中第i个元素的值。很容易可以得出状态转移方程,a[i]=a[i-1]+a[i-2]
。题目要求需要对结果进行区取余,因此正式的状态转移方程为a[i]=(a[i-1]+a[i-2])%MOD
。再看填表顺序,求i位置的状态值需要用到i-1
和i-2
位置的状态值,因此i应该从小到大填写。再看初始化,i
的取值范围是,i>=1
。因此i-1>=1,i-2>=1
得到i>=3
。因此i==1
和i==2
都需要初始化为1
。循环从i==3
开始填写。
The Biggest Water Problem
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给你一个数,让他进行巴啦啦能量,沙鲁沙鲁,小魔仙大变身,如果进行变身的数不满足条件的话,就继续让他变身。。。直到满足条件为止。
巴啦啦能量,沙鲁沙鲁,小魔仙大变身:对于一个数,把他所有位上的数字进行加和,得到新的数。
如果这个数字是个位数的话,那么他就满足条件。
输入描述:
给一个整数数字n(1<=n<=1e9)。
输出描述:
输出由n经过操作满足条件的数
示例1
输入
复制12
12
输出
复制3
3
说明
12 -> 1 + 2 = 3
示例2
输入
复制38
38
输出
复制2
2
说明
38 -> 3 + 8 = 11 -> 1 + 1 = 2
数字根:
数字根是一个数学概念,通常定义为一个正整数的所有位数相加,直到结果为一位数为止。例如,数字38
的数字根会通过下面的过程得到:3 + 8 = 11
,然后再将1 + 1
相加得到2
。因此,38
的数字根是2
。
常规模拟方法:
#include <bits/stdc++.h>
using namespace std;
using LL=long long;
int main(){
LL n;
cin>>n;
while(n/10!=0){
int sum=0;
while(n){
sum+=n%10;
n=n/10;
}
n=sum;
}
cout<<n;
}
数学方法:
数字根(
n
)=1+(
n
−1)mod9
数字根表达式的证明过程:
对于一位数n
,数字根显然就是他自己。
对于多位数,假设n
是一个多位数,di
表示n
的第i
位的数字,其中i=0,1,2,...,k,k
是最高位的索引。
因此,n
可以表示为dk*10^k+dk-1*10^(k-1)+...+d1*10^1+d0*10^0
。
10mod9=1。10^2=10*10=9*10+1*10,
因此10^2mod9=10mod9=1
。10^3=10^2*10=9*10^2+10=1
。以此类推,10^kmod9=1
。
因此nmod9==dk+dk-1+dk-2+...+d1+d0
。也就是数字根。
特殊的情况是如果n
为9
的倍数,nmod9=0
,也就是dk+dk-1+...+d1+d0=9
,数字根为9
,但是9
还需要mod9
所以计算出来的是0
。这种特殊情况数字根为9
。
为什么数字根的表达式是 数字根(n)=1+(n-1)mod9,而不是数字根(n)=nmod9?
数字根的快速计算方法通常表达为 1+(
n
−1)mod9
,这种表达方式是为了确保数字根的结果落在 1
到 9
的范围内,对于除 0
之外的所有正整数。这种计算方式主要处理的是当 n
是 9
的倍数时,我们希望数字根返回 9
而不是 0
,因为按照数字根的定义,任何正整数的数字根都不应该是 0
(除了数字 0
本身)。
如果我们直接使用 n
mod9
,那么当 n
是 9
的倍数时(例如 9, 18, 27, ...
),n
mod9
的结果会是 0
。这与数字根的定义相冲突,因为一个非零的正整数的数字根应该是一个正整数。
让我们来看一些例子来更好地理解这个问题:
对于数字 9,9mod9=0
,但我们期望的数字根应该是 9
。使用 1+(
n
−1)mod9
方法,我们得到 1+(9−1)mod9=1+8mod9=9
。
对于数字 18
,直接使用 18mod9=0
,但使用 1+(
n
−1)mod9
方法,我们得到 1+(18−1)mod9=1+17mod9=9
。
因此,使用 1+(
n
−1)mod9
的计算方法可以确保数字根始终是一个正整数,并且当 n
不是 9
的倍数时,这个公式与 n
mod9
给出相同的结果,只是在 n
是 9
的倍数时,我们得到的数字根是 9
而不是 0
。此外,对于数字 0
,直接应用 n
mod9
或 1+(
n
−1)mod9
都需要特殊处理,因为数字根的定义只有在 n
为正整数时才适用,而 0
的处理是个例外,它的数字根被定义为 0
。
#include <bits/stdc++.h>
using namespace std;
using LL=long long;
int main(){
LL n;
cin >> n;
if(n % 9 == 0) cout << 9;
else cout << n % 9;
return 0;
}
Digit Sum of N!
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给你一个整数N,定义一种操作"digit sum of N!":表示将N!的所有位数相加,得到一个sum,再将sum的所有位数相加...不断重复此过程,输出整数N操作到只有一位数的时候的值。
输入描述:
输入包含多组数据,每组数据输入一个整数N(1<=N<=10000);
输出描述:
对于每一组数据,输出对应的值。
示例1
输入
复制2 3 2018
2
3
2018
输出
复制2 6 9
2
6
9
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
while(cin>>n){
int ret=1;
for(int i=1;i<=n;i++){
if(ret>9) ret%=9;
ret*=i%9;
}
cout<<(ret%9==0?9:ret%9)<<endl;
}
}
模的运算性质:
模运算,通常表示为 "mod",在数学和计算机科学中是一种基础且重要的运算。模运算的结果是除法运算后的余数。例如,(7 mod 3 = 1),因为7除以3得到商2余1。模运算具有几个关键性质,这些性质在证明算法正确性、简化计算等方面非常有用。
模运算的性质
加法性质:
[ (a + b) mod n = ((a mod n) + (b mod n)) mod n ]
减法性质:
[ (a - b) mod n = ((a mod n) - (b mod n) + n) mod n ]
乘法性质:
[ (a * b) mod n = ((a mod n) * (b mod n)) mod n ]
幂的模运算:
[ a^k mod n = ((a mod n)^k) mod n ]
性质的证明
加法性质的证明:
假设 (a = q_1*n + r_1 ) 和 (b = q_2*n + r_2 ),其中 (q_1 ) 和 (q_2 ) 是商, (r_1 ) 和 (r_2 ) 是余数。那么:
[ a + b = (q_1 + q_2)*n + (r_1 + r_2) ]
将 (a + b ) 对 (n ) 取模,我们得到的余数是 (r_1 + r_2 ) 对 (n ) 取模的结果。这恰好等于将 (a ) 和 (b ) 分别对 (n ) 取模后相加的结果对 (n ) 再取模。
减法性质的证明:
同加法性质,考虑 (a - b = (q_1 - q_2)*n + (r_1 - r_2) )。当 (r_1 < r_2 ) 时,为了保持结果为非负,我们需要加上一个 (n ) 并对结果取模。
乘法性质的证明:
由于 (a = q_1*n + r_1 ) 和 (b = q_2*n + r_2 ),所以:
[ a * b = (q_1*n + r_1)(q_2*n + r_2) = (q_1*q_2*n^2 + (q_1*r_2 + q_2*r_1)*n + r_1*r_2) ]
显然,只有 (r_1*r_2 ) 对 (n ) 取模的结果是我们关心的,这等同于先对 (a ) 和 (b ) 分别取模,然后相乘的结果对 (n ) 取模。
幂的模运算的证明:
利用乘法性质递归地应用。考虑到 (a^k = a * a^{k-1} ),我们可以使用数学归纳法证明。
相同点:
分配性:所有这些性质都展示了模运算与加法、减法、乘法、乃至幂运算的分配性。这意味着,在进行模运算时,我们可以先对操作数单独进行模运算,然后再进行相应的加、减、乘或幂运算,其结果是一致的。
模运算的闭合性:这些性质都保证了运算结果仍然在模 n 的范围内。无论是加法、减法、乘法还是幂运算,应用模运算的结果都不会超过模的基数 n。
运算的顺序可变性:在模 n 下,这些性质说明了运算的顺序可以交换。例如,你可以先将两个数相加(或相减、相乘)然后取模,或者先取模再进行运算,结果是相同的。这为优化计算提供了灵活性。
运算简化:这些性质使得我们能够通过简化大数运算的过程来避免潜在的溢出错误,特别是在计算机编程中处理大整数时。
[NOIP2005]谁拿了最多奖学金
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
某校的惯例是在每学期的期末考试之后发放奖学金。发放的奖学金共有五种,获取的条件各自不同:
1)院士奖学金,每人8000元,期末平均成绩高于80分(>80),并且在本学期内发表1篇或1篇以上论文的学生均可获得;
2)五四奖学金,每人4000元,期末平均成绩高于85分(>85),并且班级评议成绩高于80分(>80)的学生均可获得;
3)成绩优秀奖,每人2000元,期末平均成绩高于90分(>90)的学生均可获得;
4)西部奖学金,每人1000元,期末平均成绩高于85分(>85)的西部省份学生均可获得;
5)班级贡献奖,每人850元,班级评议成绩高于80分(>80)的学生干部均可获得;
只要符合条件就可以得奖,每项奖学金的获奖人数没有限制,每名学生也可以同时获得多项奖学金。例如姚林的期末平均成绩是87分,班级评议成绩82分,同时他还是一位学生干部,那么他可以同时获得五四奖学金和班级贡献奖,奖金总数是4850元。
现在给出若干学生的相关数据,请计算哪些同学获得的奖金总数最高(假设总有同学能满足获得奖学金的条件)。
输入描述:
第一行是一个整数N(1<=N<=100),表示学生的总数。
接下来的N行每行是一位学生的数据,从左向右依次是姓名,期末平均成绩,班级评议成绩,是否是学生干部,是否是西部省份学生,以及发表的论文数。
姓名是由大小写英文字母组成的长度不超过20的字符串(不含空格);期末平均成绩和班级评议成绩都是0到100之间的整数(包括0和100);是否是学生干部和是否是西部省份学生分别用一个字符表示,Y表示是,N表示不是;发表的论文数是0到10的整数(包括0和10)。每两个相邻数据项之间用一个空格分隔。
输出描述:
包括三行:第一行是获得最多奖金的学生的姓名;
第二行是这名学生获得的奖金总数。如果有两位或两位以上的学生获得的奖金最多,输出他们之中在输入中出现最早的学生的姓名。
第三行是这N个学生获得的奖学金的总数。
示例1
输入
复制4 YaoLin 87 82 Y N 0 ChenRuiyi 88 78 N Y 1 LiXin 92 88 N N 0 ZhangQin 83 87 Y N 1
4
YaoLin 87 82 Y N 0
ChenRuiyi 88 78 N Y 1
LiXin 92 88 N N 0
ZhangQin 83 87 Y N 1
输出
复制ChenRuiyi 9000 28700
ChenRuiyi
9000
28700
#include <iostream>
using namespace std;
int main() {
int n, ag, cg, p, ts = 0, ms = 0;
string name, msn;
char il, iw;
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> name >> ag >> cg >> il >> iw >> p;
int s = 0;
if (ag > 80 && p >= 1) s += 8000;
if (ag > 85 && cg > 80) s += 4000;
if (ag > 90) s += 2000;
if (ag > 85 && iw == 'Y') s += 1000;
if (cg > 80 && il == 'Y') s += 850;
ts += s;
if (s > ms) {
ms = s;
msn = name;
}
}
cout << msn << endl;
cout << ms << endl;
cout << ts << endl;
return 0;
}
输入: 首先输入学生人数n。 然后对于每个学生,输入他们的姓名name,年龄ag,平均成绩cg,是否是学生会干部il('Y'表示是,'N'表示否),是否是西部地区学生iw('Y'表示是,'N'表示否),以及发表的论文数p。 奖学金计算: 对于每个学生,根据给定的条件计算他们能获得的奖学金总额s。 如果年龄大于80且论文数不少于1篇,奖励8000元。 如果年龄大于85且平均成绩大于80分,奖励4000元。 如果年龄大于90,奖励2000元。 如果年龄大于85且是西部地区学生,奖励1000元。 如果平均成绩大于80分且是学生会干部,奖励850元。 计算并累加所有学生的奖学金总额ts。 同时记录获得最高奖学金的学生姓名msn和金额ms。 输出: 输出获得最高奖学金的学生姓名。 输出该学生获得的奖学金金额。 输出所有学生奖学金的总和。
牛牛的汉诺塔
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
汉诺塔是一个经典问题,相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置n个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
汉诺塔以及其衍生问题往往使用递归来求解,也是学习和理解递归很好的老师。
其伪代码如下
Function Hanoi(n,a,b,c)
if n==1 then
print(a+'->'+c)
else
Hanoi(n-1,a,c,b)
print(a+'->'+c)
Hanoi(n-1,b,a,c)
end if
end Function
牛牛很快就理解了代码的意思并且写出了求解汉诺塔的程序,他现在想研究汉诺塔的规律。
请你统计以下信息:A->B,A->C,B->A,B->C,C->A,C->B的次数,以及所有移动的总步数。
输入描述:
仅一行,输入一个正整数n(1≤n≤60)(1 leq n leq 60)(1≤n≤60)表示汉诺塔的层数。
输出描述:
首先输出6行
A->B:XX
A->C:XX
B->A:XX
B->C:XX
C->A:XX
C->B:XX
分别表示每种移动情况出现的次数
最后输出一行
SUM:XX
表示所有移动情况的总和。
示例1
输入
复制3
3
输出
复制A->B:1 A->C:3 B->A:1 B->C:1 C->A:0 C->B:1 SUM:7
A->B:1
A->C:3
B->A:1
B->C:1
C->A:0
C->B:1
SUM:7
说明
伪代码所示算法的移动序列如下:
A->C
A->B
C->B
A->C
B->A
B->C
A->C
统计:
A->B出现1次
A->C出现3次
B->C出现1次
B->A出现1次
C->B出现1次
总计7次
模拟法:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
LL N = 1000005;
LL MOD = 1e4 + 7;
LL N1 = 10005;
void Hanoi(int n, char a, char b, char c, int& count_ab, int& count_ac, int& count_ba, int& count_bc, int& count_ca, int& count_cb) {
if (a == 'A' && c == 'B') count_ab++;
if (a == 'A' && c == 'C') count_ac++;
if (a == 'B' && c == 'A') count_ba++;
if (a == 'B' && c == 'C') count_bc++;
if (a == 'C' && c == 'A') count_ca++;
if (a == 'C' && c == 'B') count_cb++;
if (n == 1) {
return;
} else {
Hanoi(n - 1, a, c, b, count_ab, count_ac, count_ba, count_bc, count_ca, count_cb);
Hanoi(n - 1, b, a, c, count_ab, count_ac, count_ba, count_bc, count_ca, count_cb);
}
}
int main() {
int count_ab = 0;
int count_ac = 0;
int count_ba = 0;
int count_bc = 0;
int count_ca = 0;
int count_cb = 0;
char a = 'A', b = 'B', c = 'C';
int n;
cin>>n;
Hanoi(n, a, b, c, count_ab, count_ac, count_ba, count_bc, count_ca, count_cb);
int count=count_ab+count_ac+count_ba+count_bc+count_ca+count_cb;
cout<<"A->B:"<<count_ab<<endl;
cout<<"A->C:"<<count_ac<<endl;
cout<<"B->A:"<<count_ba<<endl;
cout<<"B->C:"<<count_bc<<endl;
cout<<"C->A:"<<count_ca<<endl;
cout<<"C->B:"<<count_cb<<endl;
cout<<"SUM:"<<count;
}
这种方法理论上可行,但是时间复杂度超时了。
数学规律:
#include<bits/stdc++.h>
using namespace std;
using LL=long long;
int main(){
int n;cin>>n;
vector<vector<LL>> a(n+1,vector<LL>(4));
a[1][1]=1;
for(int i=2;i<=n;i++){
a[i][0]=a[i-1][1]+a[i-1][2];
a[i][1]=a[i-1][0]*2+1;
a[i][2]=a[i-1][3]*2+(i-1)/2;
a[i][3]=a[i-1][2]*2;
}
cout<<"A->B:"<<a[n][0]<<endl;
cout<<"A->C:"<<a[n][1]<<endl;
cout<<"B->A:"<<a[n][2]<<endl;
cout<<"B->C:"<<a[n][0]<<endl;
cout<<"C->A:"<<a[n][3]<<endl;
cout<<"C->B:"<<a[n][2]<<endl;
cout<<"SUM:"<<a[n][0]*2+a[n][1]+a[n][2]*2+a[n][3]<<endl;
}
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!