算法竞赛入门经典_学习笔记_各例题和训练源文件
第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;
}