第二章 循环结构程序设计
2.1 for循环
例题:
输出1,2,3\(\cdots\) \(\cdots\) n的值
程序2-1
#include
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=0;i<=n;i++)
cout<
return 0;
}
提示:尽量缩短变量的定义范围,避免在其他地方调用而修改内容。
[ ] 一次循环后变量内定义的变量就会被释放。
例题2-1 aabb
输出形如aabb的4位完全平方数(即前两位数字相等,后两位数字也相等)。
程序2-2
#include
#include
using namespace std;
int main()
{
for(int a=1;a<=9;a++)
for(int b=0;b<=9;b++)
{
int n=a*1100+b*11; //在这里使用n,因此在这里定义
int m=floor(sqrt(n)+0.5); /* 函数floor(x)返回不超过x的最大整数
这里不能写成if(sqrt(n)==floor(sqrt(n))),浮 点数计算会产生误差
一般改成四舍五入,即floor(x+0.5) */
if(m*m==n)
cout<
}
return 0;
}
[ ] floor(x)等于1的区间为[1,2),floor(x+0.5)等于1的区间为[0.5,1.5)
程序2-3
#include
using namespace std;
int main()
{
for(int i=1;;i++)
{
int n=i*i;
if(n<1000) continue;
if(n>9999) break;
int hi=n/100;
int lo=n%100;
if(hi/10==hi%10 && lo/10==lo%10)
cout<
}
return 0;
}
[ ] 比较2和3程序,2是先组成aabb形式,然后判断是否为完全平方数;3则是先组成完全平方数,然后判断是否是aabb形式。2在开根号时容易产生误差,而枚举平方根法可以避免开平方的操作。
2.2 while循环和do-while循环
例题2-2 3n+1问题
对于任意大于1的自然数\(n\),若\(n\)为奇数,则将\(n\)变为3\(n\)+\(1\),否则变为\(n\)的一半。输入\(n\),输出变换的次数。
\(n\) $\leq$109。经过若干次这样的变换,一定会使n变为1。
程序2-4
#include
using namespace std;
int main()
{
long n; //当n过大时,很容易乘法溢出,要将n设为长整型。
int count=0; //count是用来充当计数器的变量
cin>>n;
while(n>1)
{
if(n%2==1) n=n*3+1;
else n/=2;
/*
cout<
*/
count++;
}
cout<
return 0;
}
例题2-3 近似计算
计算\(\frac{\pi}{4}\) =1-\(\frac{1}{3}\)+\(\frac{1}{5}\)-\(\frac{1}{7}\)+\(\cdots\) \(\cdots\),直到最后一项小于10-6。
【分析】因为在计算完一项之后才知道它是否小于10-6,所以循环终止是在计算之后,这时候就适合用do-while循环。
程序2-5
#include
#include
using namespace std;
int main()
{
double sum=0;
for(int i=0; ;i++)
{
double term=1.0/(2*i+1);
if(i%2==0) sum+=term;
else sum-=term;
if(term<1e-6) break;
}
cout<
return 0;
}
程序2-6
#include
#include
using namespace std;
int main()
{
double sum=0;
double term;
int i=0;
do
{
term=1.0/(2*i+1);
if(i%2==0)
sum+=term;
else
sum-=term;
i++;
//cout<
}while(term>1e-6);
cout<
return 0;
}
2.3 循环的代价
例题2-4 阶乘之和
输入 \(n\),计算 \(S=1!+2!+3!+\cdots+n!\) 的末六位(不含前导0)。\(n\)$\leq\(10^6^,\)n$$!$表示前 \(n\)个正整数之积。
程序2-7
#include
using namespace std;
#include
int main()
{
const int MOD=1000000;
int n,s=0;
cin>>n;
for(int i=1;i<=n;i++)
{
int f=1;
for(int j=1;j<=i;j++)
f=(f*j%MOD); //在每步计算之后对n取余,结果不变。
s=(s+f)%MOD;
//cout<25之后六位不变,因为25!后六位为零
}
cout<
cout<
return 0;
}
提示:可以使用time.h和clock()函数获得程序的运行时间。常数CLOCKS_PER_SEC和操作系统相关,请不要直接直接使用clock()的返回值,而应总是除以CLOCKS_PRE_SEC。
2.4算法竞赛中的输入输出框架
提示:在Windows下输入完毕后先按Enter键,再按Ctrl+Z键,最后再按Enter键,即可结束输入。在Linux下,输入完毕后按Ctrl+D键即可结束输入。
提示:变量在未赋值之前的值是不确定的。特别的,他不一定等于0。
[ ] 几乎所有算法竞赛的输入数据和标准答案都是保存在文件中的。
使用输入输出重定向,只需在main函数的入口处加入一下两条语句:
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
程序2-8 (重定向版)
#include
using namespace std;
#define LOCAL
#define INF 1000000000
int main()
{
#ifdef LOCAL
freopen("data.in","r",stdin); //重定向的部分写在了#ifdef和#endif中
freopen("data.out","w",stdout);
#endif
int x,n=0,min=INF,max=-INF,s=0;
cin>>x;
while(x!=0)
{
s+=x;
if(x
if(x>max) max=x;
/*
cout<
*/
n++;
cin>>x;
}
cout<
return 0;
}
程序2-9 (fopen版)
#include
#define INF 1000000000
int main()
{
FILE *fin,*fout;
fin=fopen("data.in","rb");
fout=fopen("data.out","wb");
int x,n=0,min=INF,max=-INF,s=0;
while(fscanf(fin,"%d",&x)==1)
{
s+=x;
if(x
if(x>max) max=x;
n++;
}
fprinf(fout,"%d%d%.3f\n",min,max,(double)s/n);
fclose(fin);
fclose(fout);
return 0;
}
知识点:
[ ] 先声明变量fin和fout,把scanf改成fscanf,第一个参数为fin;把printf改成fprintf,第一个参数为fout,最后执行fclose,关闭两个文件。
[ ] 如果想把fopen版的程序改写成标准输入输出,只需赋值"fin=stdin;fout=stdout"即可。
2.5注解与习题
习题1 水仙花数
输出100~999中的所有水仙花数。若3位数ABC满足\(ABC\)=\(A\)3+\(B\)3+\(C\)3,则称其为水仙花数。
#include
using namespace std;
int main()
{
//int a,b,c;
for(int i=100;i<=999;i++)
{
int a=i%10;
int b=i/10%10;
int c=i/100;
if(i==a*a*a+b*b*b+c*c*c)
cout<
}
return 0;
}
习题2 韩信点兵
韩信点兵每次让队伍以三人一队,五人一队,七人一队,只要看队尾的人数就可以知道总人数,总人数不超过一百。输入包含多组数据,每组数据包含3个非负整数 \(a\) , \(b\) , \(c\) ,表示每种队形排尾的人数( \(a\) ❤️, \(b\) <5, \(c\) <7),输出总人数的最小值(或报告无解)。已知总人数不小于10,不超过100。输入到文件结束为止。
#include
using namespace std;
int main()
{
int x,y,z; //定义最后一排的人数
int n;
while(cin>>x>>y>>z)
{
for(n=10;n<=101;n++)
{
if(n==101)
{
cout<
break;
}
if(n%3==x && n%5==y && n%7==z)
{
cout<
break;
}
}
}
return 0;
}
习题3 倒三角
输入正整数n<=20,输出一个n层的倒三角形。
#########
#######
#####
###
#
#include
using namespace std;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j
cout<
for(int k=2*(n-i)+1;k>=1;k--)
cout<
cout<
}
return 0;
}
习题4 子序列的和
输入两个正整数\(n\)
#include
using namespace std;
int main()
{
long long n,m; //当输入n,m较大时,容易溢出
double sum=0.0;
while(cin>>n>>m)
{
double sum=0.0;
for(long long i=n;i<=m;i++)
sum+=1.0/(i*i);
cout<
}
return 0;
}
习题5 分数化小数
输入正整数\(a\), \(b\), \(c\); 输出\(a\) / \(b\)的小数形式,精确到小数点后 \(c\) 位。\(a\), \(b\) $\leq$106, \(c\)$\leq$100。输入包含多组数据,结束标记为a=b=c=0。
#include
using namespace std;
#include
int main()
{
double a,b,c;
while(cin>>a>>b>>c)
{
cout<
}
return 0;
}
习题6 排列
用1,2,3 \(\cdots\) \(\cdots\) 9组成3个三位数\(abc\), \(def\) , \(ghi\) 。每个数字恰好用一次,要求 \(abc\) : \(def\) : \(ghi\)=1:2:3。
#include
using namespace std;
int add=0,mul=1;
void return13(int num)
{
int x,y,z;
x=num/100;
y=num/10%10;
z=num%10;
add+=x+y+z; //计算三位数字的和
mul*=x*y*z; //计算三位数字的乘积
}
int main()
{
for(int i=123;i<=329;i++)
{
int j=i*2;
int k=i*3;
return13(i);
return13(j);
return13(k);
if(add==45&&mul==362880) //1~9的和为45,9!=362880
cout<
add=0;
mul=1;
}
return 0;
}