循环模拟
前言
本章节依然是循环的题目,上一篇,算法练习-循环结构
1、寻找规律
分析
这里的话两种思路,一是字符串的拼接,二是把数字的每一位都分离出来,再判断,总的来说考察的知识点较为基础。这里注意一个点就是不要开三个循环,直接开一个循环表示第一个数的大小。
方法一:
python
ans = 0 # 记录结果
for a in range(123, 345): # 第一个数在123-345之间
b = 2 * a # 第二个数
c = 3 * a
res = set(str(a) + str(b) + str(c)) # 把三个数字拼起来,并利用set()方法去掉重复的数字
if len(res) == 9 and '0' not in res: # 判断是否1-9都使用了,这里注意0不能存在,因为乘了2所以有可能有0的存在
ans += 1 # 满足要求,结果+1
# print(a, b, c) 打印三个数
print(ans) # 输出结果
192 384 576
219 438 657
273 546 819
327 654 981
4
方法二:
c++
#include<iostream>
using namespace std;
int main(){
int ans=0;
for(int j, i=123;i<330;i++) {
int a[10]={0}; // 这里利用数组的下标来记录1-9的数字的使用次数
a[i/100]++;a[i%100/10]++;a[i%10]++; //分离出每一位
a[i*2/100]++;a[i*2%100/10]++;a[i*2%10]++;
a[i*3/100]++;a[i*3%100/10]++;a[i*3%10]++;
for(j=1;j<10;j++)
if(a[j]!=1) break; //判断
if(j==10)
ans++;
// cout<<i<<" "<<2*i<<" "<<3*i<<endl;
}
cout<<ans;
}
2、求质数的个数
分析
这里如果用传统的判断素数肯定会超时,这里介绍线筛法求素数,有兴趣大加可以了解其他的方法。大概原理就是初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数,把这些合数都筛掉。
python
n = int(input()) + 1 # python里面range()函数是[)也就是左闭右开,题目要取到n所以n+1
a = [True] * n # 假设全为素数
res = [] # 存放最终结果
for i in range(2, n):
if a[i]: # 如果是素数就添加到res[]里面
res.append(i)
for j in res: # 循环res[]里面的元素
k = j * i # 合数
if k >= n: # 如果大于n了,跳出
break
a[k] = False # 筛掉a[]中的合数
if i % j == 0:
break
print(len(res))
# print(res) 打印素数结果
c++ 道理一样的
#include<iostream>
using namespace std;
int a[1000000]={0};
int b[500500]={0};
int main(){
int i,j,k=0;
int n;
cin>>n;
for(i=2; i<=n;i++){
if(!a[i])
b[k++]=i;
for(int j=0;j<k &&i*b[j]<=1000000;j++){
a[i*b[j]]=1;
if(i%b[j]==0) break;
}
}
cout<< k<< endl;
}
3、偶数分解
分析
这里可以用上一个题的欧拉筛法求出n以内的素数,在分解为两个数之和
当然也可以用快速判断质数的方法来求,建议第一种方法
python
n = int(input())
a = [True] * (n + 1)
res = []
for i in range(2, n + 1): # 左闭右开
if a[i]:
res.append(i)
for j in res:
k = j * i
if k >= n + 1:
break
a[k] = False
if i % j == 0:
break
for i in range(len(res) // 2 - 1, -1, -1): # 从数组的中间开始找,保证了差尽量小
if a[res[i]] and a[n - res[i]]: # 利用a[]判断是否是质数
print(f'{res[i]} {n - res[i]}') # 满足条件输出结果
break
# print(a) 打印结果
# print(res)
测试结果
8
3 5
[True, True, True, True, False, True, False, True, False]
[2, 3, 5, 7]
方法二python:
from math import sqrt
def isPrime(num): # 定义一个快速判断质数的函数,有兴趣可以取看证明过程
flag = True
if num == 2 or num == 3:
flag = True
if num % 6 != 1 and num % 6 != 5:
flag = False
else:
sqrt_num = int(sqrt(num)) + 1
for x in range(5, sqrt_num, 6):
if num % x == 0 or num % (x + 2) == 0:
flag = False
break
flag = True
return flag
def main(n):
a=[]
for i in range(2,n//2+1): # 挨个循环
if isPrime(i) and isPrime(n-i):
x=abs(n-2*i) # 判断
a.append([x,i,n-i]) # 把两个数及其差,添加到数组里面,形成二维数组
a.sort(reverse=False,key=lambda x:x[0]) # 利用sort()里面的lambda函数对差进行排序
print(a[0][1],end=' ') # 输出答案
print(a[0][2])
n=int(input())
main(n)
建议使用第一种方法
4、奇妙的数字
分析
这里和第一道题的方法一样的,这里的话开个数组利用数组下标来记录每个数字,里面的值记录次数
方法一python
for i in range(40, 1000):
x = i ** 2
y = i ** 3
res = set(str(x) + str(y))
if len(res) == 10: # 注意题目要求有0
print(i)
break
69
方法二
这里的话我就用c++了
#include <iostream>
using namespace std;
int main(){
int x,y,j=0;
for(int i=40;i<=1000;i++){
x=i*i;y=i*i*i;
int a[10]={0};
while(x){ //分离每一位
a[x%10]++;x=x/10; //数组下标来记录每个数字,里面的值记录次数
}
while(y){
a[y%10]++;y=y/10;
}
for(j=0;j<10;j++)
if(a[j]!=1) break; //判断是否都用到了
if(j==10){
cout<<i<<endl; //输出
break;
}
}
}
5、Torry的困惑
分析
这里思路就很明确了,利用欧拉筛求得质数,在分别模运算求和最后在模运算即可,基础可以参考前面的 循环结构
def s(n): # 定义欧拉筛函数
a = [True] * 1345678 # 注意题目范围不会超过12345678
b = []
for i in range(2, 1345678):
if a[i]:
b.append(i)
for j in b:
l = i * j
if l >= 1345678:
break
a[l] = False
if i % j == 0:
break
if len(b) == n:
break
return b # 返回2-12345678的所有质数
n = int(input())
b = s(n) # b来接收返回的结果
s = 1 # 初始化变量
for i in b:
s *= i % 50000 # 模运算
print(s % 50000)
测试结果
100000
39290
6、数的页码
分析
python里面的有个count函数,但是这样会比较麻烦,所以我们可以巧妙的利用数组的下标来记录数字,里面的值代表出现的次数
python
n=int(input())
a=[0]*10
for i in range(1,n+1):
j=i
while j: # 分离数字的每一位记录在a[]中
a[j%10]+=1
j//=10
for i in range(10): # 输出
print(a[i],end=' ')
因为python解释性语言的原因,超时了,所以用c++写,理解思路就行
c++代码
#include<iostream>
using namespace std;
int a[10]={0};
int main(){
int n,i,j;
cin>>n;
for(i=1;i<=n;i++){
j=i;
while(j){
a[j%10]++;
j=j/10;
}
}
for(i=0;i<10;i++)
cout<<a[i]<<" ";
return 0;
}
7、排列式
分析
这个题和前面的1,4题大同小异,两种方法
方法一:
python
ans = 0
for i in range(11, 99):
for j in range(123, 987): # 确定前两个数的范围
x = i * j
if x > 9876: # 大于的话不满足
continue
res = list(set(list(str(i) + str(j) + str(x)))) # 利用字符串拼接
if len(res) == 9 and '0' not in res: # 注意题目里面没有使用0
ans += 1
print(ans)
方法二:
python
ans = 0
for i in range(11, 99):
for j in range(123, 987): # 确定前两位
a = [0] * 10
x = i * j
if x > 9876:
continue
a[i // 10] += 1 # 这里分离出每一位的数字,并利用数组记录,
a[i % 10] += 1
a[j // 100] += 1
a[j % 100 // 10] += 1
a[j % 10] += 1
a[x // 1000] += 1
a[x % 1000 // 100] += 1
a[x % 100 // 10] += 1
a[x % 10] += 1
v = 0
for k in range(1, 10):
if a[k] == 1:
v += 1
if v == 9:
ans += 1
print(ans)
8、校门外的树 NOIP2005
分析
这里注意读懂题目,开辟一个数组利用数组的下标来代表数,类似于桶排序的思想
python
l, m = map(int, input().split()) # 一行输入l,m空格分隔
a = []
ans = 0 # 存放最终结果
for i in range(m):
s = list(map(int, input().split()))
a.append(s) # 输入
b = [0] * 10001 # 题目中给了数据大小的范围,所以开辟10001空间的数组
a.insert(0, [0, 0]) # 防止数组下标溢出
for i in range(1, m + 1): # 循环每个区域
for j in range(a[i][0], a[i][1] + 1): # 每个区域之间有多少树
b[j] = 1 # 并将该区域记录在b[]中,数组b的下标表示树,值为1表示存在,0表示不存在
for k in range(l+1):
if b[k] == 0: # 循环b[],注意题目意思是把区域中的树移开
ans += 1
print(ans) 输出结果
c++代码:
#include <iostream>
using namespace std;
int main(){
int l,m;
int i,j,ans=0,x;
int a[110][2]={0};
int b[10001]={0};
cin>>l>>m;
for(i=1;i<=m;i++){
cin>>a[i][0]>>a[i][1];
}
for(i=1;i<=m;i++){
for(j=a[i][0];j<=a[i][1];j++)
b[j]=1;
}
for(i=0;i<=l;i++)
if(b[i]==0) ans++;
cout<<ans<<endl;
}
9、排列数
分析
简而言之就是全排列,这里可以用到python itertools标准库里面的permutations 全排列函数,这里我不过多介绍,详细请自行搜索python标准库的基本函数及用法,c++里面也有这个函数,c++里面全排列是包含在头文件里面的
python:
import itertools
n=int(input())
temp=0
s=[i for i in range(10)]
for x in itertools.permutations(s):
temp+=1
l=list(x)
if temp==n:
for i in l:
print(i,end='')
break
c++:
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
string s = "0123456789";
int n;
cin >> n;
int cnt = 1;
do {
if(cnt == n) {
cout << s;
break;
}
cnt++;
} while(next_permutation(s.begin(), s.end()));
return 0;
}
另外还有一种方法,大家可自行思考
c++:
#include <iostream>
using namespace std;
int main(){
int i,j,k,n,x;
int a[10]={1};
int b[10]={0};
for(i=1;i<10;i++){
a[i]=a[i-1]*i;
}
cin>>n;
n=n-1;
for(i=9;i>=0;i--){
x=n/a[i];
k=-1;
for(j=0;j<10;j++){
if(b[j]==0){
k++;
}
if(k==x) break;
}
b[j]++;
n=n%a[i];
cout<<j;
}
}
10、连续正整数的和
分析
这里直接简单模拟就好
python:
for i in range(1, n):
s = i # 起点用s存放
for j in range(i + 1, n): # 从i-n
if s < n: # 判断是否超出
s += j # 累加
if s > n:
break # 超出直接跳出循环
if s == n: # 判断是否符合题目条件
temp += 1
break
print(temp)
11、平方十位数
分析
和前面的题一样的思想
python:
import itertools # 导入库
s = [i for i in range(10)]
res = 0 # 记录种数
for i in itertools.permutations(s, 10): # 全排列
x = [str(k) for k in i]
temp = int(''.join(x))
if temp ** 0.5 == int(temp ** 0.5) and len(str(temp)) == 10: # 根据题目要求判断
res += 1 # 结果+1
print(res)
这种方法很慢,但是很容易想到,下面介绍另一种方法
c++:
#include <iostream>
using namespace std;
int main(){
long long i,x,ans=0; // 定义长整型
for(i=99999;i>10000;i--){ // 从大往小开始
x=i*i; //平方数
if(x<1000000000) break; //不满足10位直接跳出
int j,a[10]={0}; //定义数组a[] 利用下标记录0-9之间的数字
while(x){ //分离每一位数并存到a[]中
a[x%10]++;
x=x/10;
}
for(j=0;j<10;j++)
if(a[j]!=1) break;
if(j==10){ //判断是否0-9都使用了
cout<<i<<" "<<i*i<<endl; //输出
ans++; //结果+1
}
}
cout<<ans<<endl;
}
12、修改数组(LQB2019)
分析
这个题就是简单的模拟,按照题目要求来,定义一个res数组,第一个存放要检查的第一个数字,然后从第二个数字开始判断该数字是否在res数组里面出现过,如果出现过,就将该数+1继续判断,直到没有出现过为止。
python:
ai = list(map(int, input().split())) # 输入一行
n = ai[0] # 第一个数代表数字的长度
res = [] # 定义一个结果数组
res.append(ai[1]) # 将第一个数字添加进去
for i in range(2, len(ai)): # 从第二个数字开始
while ai[i] in res: # 用while循环判断是否出现过
ai[i] += 1 # 出现过+1
res.append(ai[i]) # 否则直接添加进去
for i in res:
print(i, end=' ') # 输出结果
c++实现:
#include <iostream>
using namespace std;
int b[1000010]={0};
int main(){
int n,i,j,k,ans=0;
int a[100005]={0};
int c[100005]={0};
cin>>n; //输入n位
for(i=1;i<=n;i++){
cin>>k;
a[i]=k;
}
c[1]=a[1];b[a[1]]++;// 这里用到了类似于桶排序的思想
for(i=2;i<=n;i++){
j=a[i];
while(b[j]!=0){
j++;
}
c[i]=j;
b[j]++;
}
for(i=1;i<=n;i++)
cout<<c[i]<<" ";
}
总结
总的来说,这篇文章的题比上两篇还是稍微难一点,欢迎大家交流学习。
下一篇 算法训练-函数