算法竞赛入门经典_学习笔记_各例题和训练源文件
第02章_循环结构程序设计
Example_0201_aabb完全平方数_先创形式再判值.cpp
Example_0201_aabb完全平方数_先取平方值再判形式.cpp
Example_0204_3n+1问题.cpp
Example_0205_阶乘之和.cpp
Exercise_0201_位数(digit).cpp
Exercise_0202_水仙花数(doffodil).cpp
Exercise_0203_韩信点兵(hanxin)_中国剩余定理.cpp
Exercise_0204_倒三角形(triangle)_等差数列.cpp
Exercise_0205_统计(stat).cpp
Exercise_0206_调和级数(harmony).cpp
Exercise_0207_近似计算(approximation).cpp
Exercise_0208_子序列的和(subsequence).cpp
Exercise_0209_分数化小数(decimal).cpp
Exercise_0210_排列(permutation)_根据条件列数字再判断.cpp
Exercise_0210_排列(permutation)_列举数字判条件.cpp
// Example_0201_aabb完全平方数_先创形式再判值.cpp /** * 题目要求:输出所有形如aabb的四位完全平方数(即前两位数字相等,后两位数字也相等)。 **/ /** * 题目及方法分析: * 题目所要求的数字有两种条件,第一种,要有形式aabb,第二种,需要可开方得整数。 * 下面可以使用先定形式,再判断是否可开方的方法。 * 所谓定形式,是指,将所有符合aabb样式的数字逐个列出来, * 然后,让这数开方,通过判断开方后的数是否为整数而判定结果。 **/ #include <iostream> #include <cmath> using std::cin; using std::cout; using std::endl; int main() { int a, b; int n; // 符合aabb形式的数 double m; // 开方后的数 for (a = 1; a <= 9; ++a){ for (b = 0; b <= 9; ++b){ n = a * 1100 + b * 11; // 创建符合aabb形式的数 m = sqrt(n); // 将其开方,保存其值 if(floor(m + 0.5) == m){// 检查整数 // floor起到舍小数取整数的值的作用,m+0.5是为了四舍五入,提高精度 cout << m << endl; } } } return 0; }
// Example_0201_aabb完全平方数_先取平方值再判形式.cpp /** * 题目要求:输出所有形如aabb的四位完全平方数(即前两位数字相等,后两位数字也相等)。 **/ /** * 题目及方法分析: * 题目所要求的数字有两种条件,第一种,要有形式aabb,第二种,需要可开方得整数。 * 下面使用逆向思维解出此题,先将平方得到的数列出来,然后判断是否符合aabb的形式。 * 这里平方数的得到方法很简单,只需要将其从1开始平方,直到这数大于9999即停止。 * 然后,判断是否符合aabb的形式,可以将数分解为aa, bb,然后再将aa, bb单个分解比较。 **/ #include <iostream> using std::cin; using std::cout; using std::endl; int main() { int x, n; int aa, bb; for (x = 1, n = 1; n <= 9999; ++x, n = x * x){ if (n < 1000){ continue; } aa = n / 100; // 分解出aa bb = n % 100; // 分解出bb if ((aa / 10 == aa % 10) && (bb / 10 == bb % 10)){ // 再分别从aa和bb中分解 cout << n << endl; } } return 0; }
// Example_0204_3n+1问题.cpp /** * 题目名称:3n+1问题 * 题目描述:对于任意大于1的自然数,若n为奇数,则将n变为3n+1,否则变为n的一半。 * 经过若干次这样的变换后,一定会使n变为1。输入n,输出变换的次数。 n <= pow(10,9) * 例如: 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1 * 输入: 3 * 输出: 7 **/ /** * 题目及方法分析: * 题目要完成的工作要么是3n+1,要么就是n/2,也就是说,在统计次数时,只需要用上一个if-else语句就行了。 * 另外,需要注意的是,题目特意提出了n的取值范围是10的9次方, * 需要知道的是,int的取值范围只到10的10次方,如果多次*3+1的话,很容易溢出, * 那么,我们可以考虑使用double型的数据作为这个n,因为它的最大值可达到10的308次方, * 附:<cmath>中的函数 double fmod( double x, double y );可以返回double型数据的余数。 **/ #include <iostream> #include <cmath> using std::cin; using std::cout; using std::endl; int main() { double n(0); int count(0); cin >> n; while(1 != n){ if (0 != fmod(n, 2)){ n = 3 * n + 1; } else{ n /= 2; } ++count; } cout << count << endl; return 0; }
// Example_0205_阶乘之和.cpp /** * 题目名称:阶乘之和 * 题目描述:输入n,计算S = 1! + 2! + 3! + ... + n!的末6位(不含前导0)。 n <= pow(10,6); n!表示前n个正整数之积。 * 样例输入: 10 * 样例输出: 37913 **/ #include <iostream> #include <cmath> using std::cin; using std::cout; using std::endl; int main() { long n(0); double s(0); cin >> n; for (int i = 1; i <= n; ++i){ long one(1); for(int j = 1; j <= i; ++j){ one *= j; } s += one; } cout.setf(std::ios_base::fixed, std::ios_base::floatfield); cout.precision(0); cout << s << endl; return 0; }
// Exercise_0201_位数(digit).cpp /** * 题目名称:位数(digit) * 题目描述:输入一个不超过pow(10, 9)的正整数,输出它的位数。 * 样例输入: 12735 * 样例输出: 5 * 特别说明:请不要使用任何数学函数,只用四则运算和循环语句实现。 **/ /** * 题目分析:只需要使用整型除法,逐个位数判断是否为零,是否存在数值。 * 当除数除被除数时不为零时,则位数存在数值。 **/ #include <iostream> using namespace std; int main(void) { int test; cin >> test; int count(0); for (int i = 1; i < 1000000000; i *= 10){ if (0 != test / i){ ++count; } } cout << count << endl; return 0; }
// Exercise_0202_水仙花数(doffodil).cpp /** * 题目名称:水仙花数(doffodil) * 题目描述:输出 100~999 中的所有水仙花数。若3位数满足 ABC = A*A*A + B*B*B + C*C*C,则称水仙花数。 **/ #include <iostream> using namespace std; inline int threeTimes(int n) { return n * n * n; } int main(void) { for (int i = 100; i < 1000; ++i){ if (i == threeTimes(i / 100) + threeTimes(i / 10 % 10) + threeTimes(i % 10)){ cout << i << endl; } } return 0; }
// Exercise_0203_韩信点兵(hanxin)_中国剩余定理.cpp /** * 题目名称:韩信点兵(hanxin) * 题目描述: * 相传韩信才智过人,从不直接清点自己军队的人数,只要让士兵先后以三人一排、 * 五人一排、七人一排地变换队形,而他每次只掠一眼队伍的排尾就知道总人数了。 * 输入3个非负整数a, b, c, 表示每种队形排尾的人数(a < 3, b < 5, c < 7), * 输出总人数的最小值(或报告无解)。已知人数不小于10, 不超过100。 * 样例输入: 2 1 6 * 样例输出: 41 * 样例输入: 2 1 3 * 样例输出: No answer **/ /** * 题目分析:可直接用中国剩余定理。 * x % 3 = a; * x % 5 = b; * x % 7 = c; * 用中国剩余定理可得公式 x =(70*a + 21*b + 15*c) % 105 * 70是5与7的倍数,而用3除余1; * 21是3与7的倍数,而用5除余1; * 15是3与5的倍数,而用7除余1; * 105是它们三者的最小公倍数n; * 使用这定理有两前提,其一:除数都为素数,其二:所求数需小于最小公倍数n。 **/ #include <iostream> using namespace std; int main(void) { int a, b, c; cin >> a >> b >> c; bool mark = false; for (int i = 10; i < 100; ++i){ if (i == (70*a + 21*b + 15*c) % 105){ cout << i << endl; mark = true; break; } } if (false == mark){ cout << "No answer" << endl; } return 0; }
// Exercise_0204_倒三角形(triangle)_等差数列.cpp /** * 题目名称:倒三角形(triangle) * 题目描述:输入一个正整数 n<=20, 输出一个n层的倒三角形。 * 样例输入:5 * 样例输出: * # # # # # # # # # * # # # # # # # * # # # # # * # # # * # **/ /** * 题目分析:观察倒三角形与输入的正整数n的规律。(等差数列) * 从整体来看,这是一个n*n的矩形。 * 从有图案的地方来看,将三角形正着来看的时候, * 第1行1个#, 第2行3个#,第3行5个#,第4行7个#,第5行9个#, * a = a1 + (n-1)*2 * 现在再把三角形倒着来看,研究一下左边空白的地方, * 左边空白地方很容易填充,相当于一个初值为0,步长为1的数列。 **/ #include <iostream> using namespace std; int main() { int n; cin >> n; int t = n; // 记录到了倒序的第几行. for (int i = 0; i < n; ++i, --t){ int j = i; while (j--){ // 先填充空白 cout << " "; } int temp = 1 + (t - 1) * 2; while(temp--){ // 填充三角形 cout << "#"; } cout << endl; // 一行完毕即换行 } return 0; }
// Exercise_0205_统计(stat).cpp /** * 题目名称:统计(stat) * 题目描述:输入一个正整数n,然后读取n个正整数a1, a2, ..., an, 最后再读一个正整数m。 * 统计a1, a2, ..., an 中有多少个整数的值小于m。 **/ #include <iostream> #include <vector> #include <iterator> using std::cin; using std::cout; using std::endl; using std::vector; using std::iterator; int main() { vector <int> remember; int n; cin >> n; for (int i = 0; i < n; ++i){ int x; cin >> x; remember.push_back(x); } int m; cin >> m; int count = 0; for (vector<int>::iterator beg = remember.begin(); beg != remember.end(); ++beg){ if ( *beg <= m){ ++count; } } cout << count << endl; return 0; }
// Exercise_0206_调和级数(harmony).cpp /** * 题目名称:调和级数(harmony) * 题目描述:输入正整数n,输出H(n) = 1 + 1/2 + 1/3 + ... + 1/n 的值,保留3位小数。 * 样例输入: 3 * 样例输出: 1.833 **/ #include <iostream> using namespace std; int main() { int n; cin >> n; double sum = 0; double count = 1.0; for (int i = 1; i <= n; ++i){ sum += 1 / count; // 注意:除的时候不要用整型来除 count += 1; } cout.setf(ios_base::fixed, ios_base::floatfield); cout.precision(3); cout << sum << endl; return 0; }
// Exercise_0207_近似计算(approximation).cpp /** * 题目名称:近似计算(approximation) * 题目描述:计算 pi / 4 = 1 - 1/3 + 1/5 - 1/7 +..., 直到最后一项小于pow(10, -6);得出pi. **/ #include <iostream> using namespace std; int main() { double n = 1.00; double sum = 0; double i = 1; int j = 1; for (; n >= 0.0000001; i += 2, ++j){ n = 1.0 / i; if( 0 != j % 2){ sum += n; } else{ sum -= n; } } cout.setf(ios_base::fixed, ios_base::floatfield); cout.precision(3); cout << sum * 4 << endl; return 0; }
// Exercise_0208_子序列的和(subsequence).cpp /** * 题目名称:子序列的和(subsequence) * 题目描述:输入两个正整数n < m < pow(10, 6), 输出 1/n*n + 1/(n+1)*(n+1) + ... + 1/m*m,保留5位小数。 * 样例输入:2 4 * 样例输出:0.42361 * 样例输入:65536 655360 * 样例输出:0.00001 **/ #include <iostream> using namespace std; int main() { double n, m; cin >> n >> m; double sum = 0; while (n != m){ sum += 1.0 / (n * n); n += 1.0; } sum += 1.0 / (m * m); cout.setf(ios_base::fixed, ios_base::floatfield); cout.precision(5); cout << sum << endl; return 0; }
// Exercise_0209_分数化小数(decimal).cpp /** * 题目名称:分数化小数(decimal) * 题目描述:输入正整数a, b, c,输出 a/b 的小数形式,精确到小数点后c位。 a,b <=pow(10, 6), c <= 100. * 样例输入:1 6 4 * 样例输出:0.1667. **/ #include <iostream> using namespace std; int main() { double a, b; int c; cin >> a >> b >> c; cout.setf(ios_base::fixed, ios_base::floatfield); cout.precision(c); cout << a / b << endl; return 0; }
// Exercise_0210_排列(permutation)_根据条件列数字再判断.cpp /** * 题目名称:排列(permutation) * 题目描述:用1, 2, 3, ..., 9 组成3个位数 abc, def 和 ghi, 每个数字恰好使用一次。 * 要求: abc: def: ghi = 1: 2: 3.输出所有解。 * 结果: * 192 : 384 : 576 * 219 : 438 : 657 * 273 : 546 : 819 * 327 : 654 : 981 **/ /** * 题目分析:这个题目与aabb的完全平方的那题有点相似,思路也是有两个, * 第二种方法,根据条件列数字再判断,(运行时间:0.590s) * 这次进行的循环次数相对上一种方法少,这次是直接以一个三位数为基准作为判断条件。 * 将题目中的abc,从123开始执行循环,直到330停止,这里选择边界为330是因为当其为333的三倍时会超过三位数,不符题意。 * 而题目又要求不能重复数字,所以"330”也是不可能的,329会有可能符合条件,所以,选择不等于330作为终止条件。 * 在循环中,先将三位数的逐个数字提取出来,然后使用数组compare存放,作为标记,让接下来的数字与数组内的数字进行比较,避免数字重复的事件出现。 * 第一组数abc判断完毕后,开始进行第二组数,而第二组数只需要将第一组数乘2即可得,然后再按第一组数的判断方法判断是否数字重复。 * 第三组数是abc的三倍,判断方法与前两种方法一样,当所有的数字都不重复的时候,可以直接将这三组数的结果输出了。 * 这里的不用进行 abc:def:ghi的比例判断,是因为在进行循环条件的同时已经利用了这个规则创建def及ghi这个数,这也是这种方法高效的原因。 **/ #include <iostream> using namespace std; inline bool haveSame(const int t, const int (&c)[10]) { for (int i = 0; i != 10; ++i){ if (t == c[i]){ return true; } } return false; } int main() { int a(0), b(0), c(0), d(0), e(0), f(0), g(0), h(0), i(0); int compare[10]; for (int j = 123; j < 330; ++j){ compare[0] = 0; compare[1] = 0; compare[2] = 0; compare[3] = 0; compare[4] = 0; compare[5] = 0; compare[6] = 0; compare[7] = 0; compare[8] = 0; compare[9] = 0; a = j / 100; compare[1] = a; b = j % 100 / 10; if (haveSame(b, compare)){ continue; } compare[2] = b; c = j % 10; if (haveSame(c, compare)){ continue; } compare[3] = c; int j2 = j * 2; d = j2 / 100; if (haveSame(d, compare)){ continue; } compare[4] = d; e = j2 % 100 / 10; if (haveSame(e, compare)){ continue; } compare[5] = e; f = j2 % 10; if (haveSame(f, compare)){ continue; } compare[6] = f; int j3 = j * 3; g = j3 / 100; if (haveSame(g, compare)){ continue; } compare[7] = g; h = j3 % 100 / 10; if (haveSame(h, compare)){ continue; } compare[8] = h; i = j3 % 10; if (haveSame(i, compare)){ continue; } compare[9] = i; cout << j << " : " << j2 << " : " << j3 << endl; } return 0; }
// Exercise_0210_排列(permutation)_列举数字判条件.cpp /** * 题目名称:排列(permutation) * 题目描述:用1, 2, 3, ..., 9 组成3个位数 abc, def 和 ghi, 每个数字恰好使用一次。 * 要求: abc: def: ghi = 1: 2: 3.输出所有解。 * 结果: * 192 : 384 : 576 * 219 : 438 : 657 * 273 : 546 : 819 * 327 : 654 : 981 **/ /** * 题目分析:这个题目与aabb的完全平方的那题有点相似,思路也是有两个, * 第一种方法,逐个数字列举,然后看其是否符合条件, * 这里为a~i每个数字都申请了一个变量,然后,从一个整数中拆分出来, * 申请了一个数组compare[10],用于记忆前面的变量用过哪些数字,这是为了筛选出不重复的数字。 * 在循环中判断,一个个数字拆分,放于相对应的变量a~i中,当变量不符合条件则进行下次循环。 * 最后再将这些拆分出来的不重复数字,再次组合成三个变量,筛选是否符合1:2:3的条件。 * 这种方法的效率比较低,因为要判断300000000个数左右。(运行时间:43.510s) **/ #include <iostream> using namespace std; inline bool haveSame(const int t, const int (&c)[10]) { for (int i = 0; i != 10; ++i){ if (t == c[i]){ return true; } } return false; } int main() { int a(0), b(0), c(0), d(0), e(0), f(0), g(0), h(0), i(0); int compare[10]; for (int j = 123456789; j < 400000000; ++j){ compare[0] = 0; compare[1] = 0; compare[2] = 0; compare[3] = 0; compare[4] = 0; compare[5] = 0; compare[6] = 0; compare[7] = 0; compare[8] = 0; compare[9] = 0; a = j / 100000000; if (haveSame(a, compare)){ continue; } compare[1] = a; b = j % 100000000 / 10000000; if (haveSame(b, compare)){ continue; } compare[2] = b; c = j % 10000000 / 1000000; if (haveSame(c, compare)){ continue; } compare[3] = c; d = j % 1000000 / 100000; if (haveSame(d, compare)){ continue; } compare[4] = d; e = j % 100000 / 10000; if (haveSame(e, compare)){ continue; } compare[5] = e; f = j % 10000 / 1000; if (haveSame(f, compare)){ continue; } compare[6] = f; g = j % 1000 / 100; if (haveSame(g, compare)){ continue; } compare[7] = g; h = j % 100 / 10; if (haveSame(h, compare)){ continue; } compare[8] = h; i = j % 10; if (haveSame(i, compare)){ continue; } compare[9] = i; int abc = a * 100 + b * 10 + c; int def = d * 100 + e * 10 + f; int ghi = g * 100 + h * 10 + i; if (def == abc * 2 && ghi == abc * 3){ cout << abc << " : " << def << " : " << ghi << endl; } } return 0; }