第02章_循环结构程序设计

算法竞赛入门经典_学习笔记_各例题和训练源文件

 

第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;
}



 

你可能感兴趣的:(第02章_循环结构程序设计)