算法训练-循环模拟


前言

本章节依然是循环的题目,上一篇,算法练习-循环结构


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]<<" ";
}

总结

总的来说,这篇文章的题比上两篇还是稍微难一点,欢迎大家交流学习。
下一篇 算法训练-函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值