枚举和尺取法

1.到 X 星球旅行的游客都被发给一个整数,作为游客编号。

X 星的国王有个怪癖,他只喜欢数字 3,53,5 和 77。

国王规定,游客的编号如果只含有因子:3,5,73,5,7,就可以获得一份奖品。

我们来看前 1010 个幸运数字是:

35791521252735453579152125273545

因而第 1111 个幸运数字是: 4949

小明领到了一个幸运数字 5908470958750559084709587505,他去领奖的时候,人家要求他准确地说出这是第几个幸运数字,否则领不到奖品。

请你帮小明计算一下,5908470958750559084709587505 是第几个幸运数字。

思路:

暴力枚举。这个系列的数可以表 示为3 ^  i × 5 ^  j × 7 ^  k ,枚举所有不超过范围的 i j k 组合。代码中最小的3 ^  50 肯定超过 59084709587505。
题解:
cnt = 0
for i in range(50):
  for k in range(50):
    for j in range(50):
      a = 3**i
      b = 5**k
      c = 7**j
      if a*b*c<=59084709587505:
        cnt+=1
print(cnt-1)  #这里的减1是因为当i,j,k=0的时候这三个数相乘=1,1不是幸运数字

2

python模板:

order = [0] * 20
chosen = [0] * 20
n = 0
def calc(x):
    if x == n + 1:
        ansTem = ''
        for i in range(1, n + 1):
            print(order[i], end=' ')
        print('')
        return
    for i in range(1, n+1):
        if chosen[i] == 1:
            continue
        order[x] = i
        chosen[i] = 1
        calc(x+1)
        chosen[i] = 0
        order[x] = 0
if __name__ == '__main__':
    n = int(input())
    # print(name)
    calc(1)

 这里用到了DFS的模板

 针对DFS我们再看一道题

现在小学的数学题目也不是那么好玩的。 看看这个寒假作业:

   □ + □ = □
   □ - □ = □
   □ × □ = □
   □ ÷ □ = □

每个方块代表 1~13 中的某一个数字,但不能重复。

比如:

 6  + 7 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

以及:

 7  + 6 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5

就算两种解法。(加法,乘法交换律后算不同的方案)

你一共找到了多少种方案?

import os
import sys

b, vis = [0]*15, [0]*15
ans = 0
def dfs(num):
    global ans
    if num == 13:
        if b[10] == b[11]*b[12]:  #把除法改写成乘法,因为除法运行比较麻烦 
            ans += 1   #更新答案
        return
#剪枝过程,如果前几个式子无法满足,那后面九个数组合而成的式子也无法满足
    if num == 4 and b[1] + b[2] != b[3]:  
            return
    if num == 7 and b[4] - b[5] != b[6]:
            return
    if num == 10 and b[7] * b[8] != b[9]:
            return
    for i in range(1, 14):
        if vis[i] == 0:  #表示该层数还未使用
            b[num] = i   
            vis[i] = 1   #表示该层数已经计算过了,需要进入下一层
            dfs(num+1)   #进入下一层
            vis[i] = 0   #回溯,重新回到if语句那一行

dfs(1) #一般这里填1
print(ans)

输出结果:

 3. 排列函数permutations   这个函数在itertools模块里。

功能:连续返回由 iterable序列中的元素生成的长度为r的排列。

如果r未指定或为None,r默认设置为 iterable 的长度,即生成包含所有元素的全排列。

代码示例:

from itertools import permutations
s = ['a', 'b', 'c']
for i in permutations(s, 2):
    a = i[0] + i[1]
    print(a)

结果:

不过这个方法慎用,经常会有超时的情况。

来看道题目吧

如果用 a b c d 这 4 个字母组成一个串,有 4!=24 种,如果把它们排个序,每个串都对应一个序号:

abcd 0

abdc 1

acbd 2

acdb 3

adbc 4

adcb 5

bacd 6

badc 7

bcad 8

bcda 9

bdac 10

bdca 11

cabd 12

cadb 13

cbad 14

cbda 15

cdab 16

cdba 17

⋯⋯

现在有不多于 10 个两两不同的小写字母,给出它们组成的串,你能求出该串在所有排列中的序号吗?

输入描述

输入一行,一个串。

输出描述

输出一行,一个整数,表示该串在其字母所有排列生成的串中的序号。注意:最小的序号是 0。

输入输出样例

示例

输入

bdca

输出

11

题解:

先将输入的东西搞成一个列表,再用sort进行排序,最后用permutations()进行全排列

from itertools import*
olds = input()
news = list(olds)
news.sort()
cnt = 0
for ele in permutations(news):
    a = ''.join(ele)
    if olds == a:
        print(cnt)
        break
    cnt += 1

4.尺取法

尺取法是一种线性的高效率算法。记 (L, R ) 为一个序列内以L为起点的最短合法区间,如果R随L的增大而增大的,就可以使用尺取法。具体的做法是不断的枚举 L,同时求出R。因为 R 随 L增大而增大,所以总时间复杂度为 O(n)

核心就是L 和 R

对于一些需要考虑运行时间的题,尺取法是个不错的选择

反向扫描:i、j方向相反,i从头到尾,j从尾到头,在中间相会。 “左右指针”

 

同向扫描:i、j方向相同,都从头到尾,速度不同,例如让j跑在i前面。“快慢指针”

 来看几道题目

1.题目描述

给定一个长度为 n 的字符串 S。请你判断字符串 S 是否回文。

输入描述

输入仅 11行包含一个字符串 S。

1≤∣S∣≤10^6,保证 S 只包含大小写、字母。

输出描述

若字符串 S 为回文串,则输出 Y,否则输出 N。

输入输出样例

示例 1

输入

abcba

输出

Y

示例 2

输入

abcbb

输出

N

思路:反向扫描的两个指针i、j,指针i从左向右扫描,指针j从右向左扫描,在中间i < j处相遇并停止 

伪代码:

输入s
i=0
j=len(s)-1
if i==j:  print('Y')
else:
    while s[i]==s[j]:
        i+=1
        j-=1     #让i和j相互靠近
        if j<=i:
            print('Y')
            break
    else:
        print("N")

2.【问题描述】输入n个整数,放在数组a[]中。找出其中的两个数,它们之和等于整数m。

思路:

(1)对数组从小到大排序;

(2)i和j分别指向头和尾,i和j向中间移动:    

   a[i] + a[j] > m,让j减1:大的变小  

   a[i] + a[j] < m,让i加1:小的变大      

   直至a[i] + a[j] = m

3.给定一个长度为 n 的序列 a1​,a2​,⋯,an​ 和一个常数 S。

对于一个连续区间如果它的区间和大于或等于 S,则称它为美丽的区间。

对于一个美丽的区间,如果其区间长度越短,它就越美丽。

请你从序列中找出最美丽的区间​。

输入描述

第一行包含两个整数n,S,其含义如题所述。

接下来一行包含 n 个整数,分别表示a1​,a2​,⋯,an​。

10≤N≤10^5,1×ai​≤10^4,1≤S≤10^8。

输出描述

输出共一行,包含一个整数,表示最美丽的区间的长度。

若不存在任何美丽的区间,则输出 0。

输入输出样例

示例 1

输入

5 6
1 2 3 4 5

输出

2

最美丽区间:1. 左端点与右端点的差值越小,这个区间就越美丽

                      2.左端点与右端点的和大于或等于s

题解:

n, s = map(int, input().split())
a = list(map(int, input().split()))  #输入多个数字的时候用这种形式输入
i, j = 0, 0
sum = 0
ans = 1e8      #初始化一个较大的数
while i < len(a):  #遍历整个列表
    if sum < s:  #如果区间小于s,则一直相加
        sum += a[i] #区间加和
        i += 1
    else:
        ans = min(i - j, ans)  #寻找最美丽区间
        sum -= a[j]
        j += 1
if ans == 0:   #i和j不重合
    print('0')
else:
    print(ans)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值