蓝桥杯Python组排列和组合、二进制讲解

news2025/1/21 15:34:45

目录

一、排列

1、Python 的排列函数 permutations()

2、permutations() 按什么顺序输出序列(重要⭐)

3、易错点

二、组合

1、Python的组合函数combinations()

2、注意内容

三、手写排列和组合代码

1、手写排列代码(暴力法)

2、手写组合代码(暴力法)

3、手写组合代码(二进制法)

4、输出n个数中任意m个数的组合

5、输出 n 个数的任意组合(所有子集)

四、例题讲解

1、排列序数(lanqiaoOJ题号269)

1)用 sort() 函数

2)用 sorted() 函数。sorted() 能直接在字符串的内部排序

2、拼数(lanqiaoOJ题号782)

3、火星人(lanqiaoOJ题号572)

4、带分数(lanqiaoOJ题号208)


一、排列

1、Python 的排列函数 permutations()

  • itertools.permutations(iterable, r=None)
  • 功能:连续返回由 iterable 序列中的元素生成的长度为 r 的排列。
  • 如果 r 未指定或为 None,默认设置为 iterable 的长度,即生成包含所有元素的全排列。
from itertools import *
s=['a','b','c']
for element in permutations(s,2):
    # print(element)
    a=element[0]+element[1]
    # 或者这样写:a=''.join(element)
    print(a,end=' ')

2、permutations() 按什么顺序输出序列(重要⭐)

  • 答:按元素的位置顺序输出元素的排列,也就是说,输出排列的顺序是位置的字典序。例如 s = ['b','a','c'],执行 permutations(s),输出 "bac bca abc acb cba cab" ,并不是按字符的字典序输出排列,而是按位置顺序输出。
  • s=['b','a','c'] 的 3 个元素的位置是 'b'=1、'a'=2、'c'=3,输出的排列 “bac bca abc acb cba cab”,按位置表示就是“123 132 213 231 312 321”,这是按从小到大的顺序输出的。
from itertools import *
s=['b','a','c']
for element in permutations(s):
    #print(element)
    a=''.join(element)
    print(a,end=' ')

如果有相同的元素,不同位置的元素被认为不同。例如 s=['a', 'a', 'c'],执行 permutations(s),输出 "ааc aca aaс аcа cаа cаа".

from itertools import *
s=['a','a','c']
for element in permutations(s):
    #print(element)
    a=''.join(element)
    print(a,end=' ')

3、易错点

初学者容易犯一个错误,把元素当成了位置。例如 s = ['1', '3', '2'],执行 permutations(s),输出“132 123 312 321 213 231",看起来很乱,实际上是按 3 个元素的位置 '1'=1、'3'=2、'2'=3 输出有序的排列的。若需要输出看起来正常的 “123 132 213 231 312 321”,可以把 s=['1','3','2'] 先用 sort() 排序为 ['1', '2', '3'],再执行 permutations()。

from itertools import *
s=['1','3','2']
for element in permutations(s):
    #print(element)
    a=''.join(element)
    print(a,end=' ')

如何输出看起来正常的 “123 132 213 231 312 321”?

先把 s = ['1', '3', '2'],用 sort() 排序为 ['1', '2', '3'],再执行 permutations()

from itertools import *
s=['1','3','2']
s.sort()
for element in permutations(s):
    #print(element)
    a=''.join(element)
    print(a,end=' ')

二、组合

1、Python的组合函数combinations()

permutations() 输出的是排列,元素的排列是分先后的,“123” 和 “321” 不同。但是有时只需要输出组合,不用分先后,此时可以用 combinations() 函数。

from itertools import *
s=['1','3','2']
for element in combinations(s,2):
    #print(element)
    a=''.join(element)
    print(a,end=' ')

2、注意内容

如果序列 s 中有相同的字符,且 s 是用 [ ] 表示的数组,那么 s 中不同位置的元素被认为不同。

from itertools import *
s=['1','1','3','2']
for element in combinations(s,2):
    #print(element)
    a=''.join(element)
    print(a,end=' ')

如果要去重怎么办?用集合,s用 { } 表示。

from itertools import *
s={'1','1','3','2'}
for element in combinations(s,2):
    #print(element)
    a=''.join(element)
    print(a,end=' ')    # 多次输出的顺序是不定的

如果要去重且输出按字典序:先用 set() 去重,再转为 list,再排序

from itertools import *
s={'1','1','3','2'}
t=list(set(s))
t.sort()
#print(t)
for element in combinations(t,2):
    #print(element)
    a=''.join(element)
    print(a,end=' ')    # 多次输出的顺序是不定的

三、手写排列和组合代码

  • 在某些场景下,系统排列函数并不能用,需要手写代码实现排列组合。
  • 在 “DFS与排列组合” 中给出了基于 DFS 的手写方法。
  • 下面给出几种简单的手写方法。

1、手写排列代码(暴力法)

从 {1,2,3,4} 中选 3 个的排列,有 24 种。最简单直接无技巧的手写排列这样写:

s=[1,2,3,4]
for i in range(4):
    for j in range(4):
        if j!=i:
            for k in range(4):
                if k!=j and k!=i:
                    print("%d%d%d"%(s[i],s[j],s[k]),end=",")

【优缺点】简单且效果很好,但是非常笨拙。如果写 5 个以上的数的排列组合,代码冗长无趣。

2、手写组合代码(暴力法)

  • 排列数需要分先后,组合数不分先后。
  • 把求组合的代码,去掉 if,然后按从小到大打印即可。
  • 从 {1,2, 3,4} 中选 3 个的组合有 4 种。
s=[1,2,3,4]
for i in range(4):  
    for j in range(i+1,4):
        for k in range(j+1,4):
            print("%d%d%d"%(s[i],s[j],s[k]),end=",")

3、手写组合代码(二进制法)

一个包含 n 个元素的集合 {a0, a1, a2, a3, ..., an-1},它的子集有 {φ}, {a0}, {a1}, {a2}, ..., {a0, a1, a2}, ..., {a0, a1, a2, a3, ..., an-1},共 2n 个。

用二进制的概念进行对照是最直观的,子集正好对应了二进制。例如 n=3 的集合 {a0, a1, a2},它的子集和二进制数的对应关系是:

每个子集对应了一个二进制数。二进制数中的每个 1,对应了子集中的某个元素。而且,子集中的元素,是不分先后的,这正符合组合的要求。

下面的代码通过处理每个二进制数中的 1,打印出了所有的子集。

#include<bits/stdc++.h>
using namespace std;
int a[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14};

void print_subset(int n){
	for(int i=0;i<(1<<n);i++){
		for(int j=0;j<n;j++){  //打印一个子集,即打印 i 的二进制数中所有的 1 
			if(i&(1<<j)){	   //从 i 的最低位开始,逐个检查每一位,如果是 1,打印 
				cout<<a[j]<<" ";
			}
		}	
		cout<<"; ";
	}
}

int main() {
	int n=3;
	print_subset(n);
	return 0;
}

输出:

; 1 ; 2 ; 1 2 ; 3 ; 1 3 ; 2 3 ; 1 2 3 ;

4、输出n个数中任意m个数的组合

  • 根据上面子集生成的二进制方法,一个子集对应一个二进制数:一个有m个元素的子集,它对应的二进制数中有m个1。
  • 所以问题转化为:查找 1 的个数为 m 个的二进制数,这些二进制数对应了需要打印的子集。
  • 如何判断二进制数中 1 的个数为 m 个? 简单的方法是对这个 n 位的二进制数逐位检查,共需要检查 n 次。

有一个更快的方法,可以直接定位二进制数中 1 的位置,跳过中间的 0。

用到一个神奇操作:k = k & (k-1),功能是消除 k 的二进制数的最后一个 1。连续进行这个操作,每次消除一个 1,直到全部消除,操作次数就是 1 的个数。例如二进制数 1011,经过连续 3 次操作后,所有 1 都消除了:

1011 & (1011 - 1) = 1011 & 1010 = 1010

1010 & (1010 - 1) = 1010 & 1001 = 1000

1000 & (1000 - 1) = 1000 & 0111 = 0000

利用这个操作,可以计算出二进制数中 1 的个数。用 num 统计1的个数,具体步骤是:

1)用 k=k & (k-1) 清除 k 的最后一个 1;

2)num++;

3)继续上述操作,直到k=0。

5、输出 n 个数的任意组合(所有子集)

输出按字典序输出,从小到大。(下面的代码已经说明了二进制的精髓)

a=[1,2,3,4,5,6,7,8,9,10,11,12,13,14]
def print_set(n,m):
    for i in range(2**n):   #2**n可以写成1<<n
        num,k=0,i           #num统计i中1的个数,k用来处理i
        while k>0:
            k=k&(k-1)       #清除k的最后一个1
            num+=1
        if num==m:
            for j in range(n):
                if i&(2**j):
                    print(a[j],end="")
            print(";",end='')
n,m=4,3
print_set(n,m)

四、例题讲解

1、排列序数(lanqiaoOJ题号269)

【题目描述】

如果用 a b c d 这 4 个字母组成一个串,有 4!=24 种。现在有不多于 10 个两两不同的小写字母,给出它们组成的串,你能求出该串在所有排列中的序号吗?

【输入描述】输入一行,一个串。

【输出描述】输出一行,一个整数,表示该串在其字母所有排列生成的串中的序号。注意:最小的序号是 0。下面给出两种代码,分别用 sort() 和 sorted() 排序,然后用permutions()求排列。

1)用 sort() 函数

sort() 不能直接在字符串内部排序。可以这样:把字符串转换成数组,对数组排序后,再转换回字符串,就得到了最小字符串。

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

2)用 sorted() 函数。sorted() 能直接在字符串的内部排序

from itertools import *
olds=input()
news=sorted(olds)
print(olds,news)
a=[]
for ele in permutations(news):
    a.append(ele)
print(a.index(tuple(olds)))

2、拼数(lanqiaoOJ题号782)

【题目描述】

设有 n 个正整数 a1, a2, ..., an,将它们联接成一排,相邻数字首尾相接,组成一个最大的整数。 n<20。

最简单粗暴的方法,是先得到这 n 个整数的所有排列,然后找其中最大的。但是这个方法的复杂度是 O(n!),当 n =20 时,有 20! =2×1018 种排列,超时。

from itertools import *
N=int(input())
ans=""
nums=list(map(str,input().split())) #按字符的形式读入
for ele in permutations(nums):  #每次输出一个全排列
    a="".join(ele)
    #print(a)
    if ans<a:
        ans=a       #在所有串中找最大的
print(ans)
    

暴力排列不行,可以用排序吗? 本题不能直接对数字排序然后首尾相接,例如“7, 13”,应该输出“713”,而不是“137”。注意到这其实是按两个数字组合的字典序排序,也就是把数字看成字符串来排序。本题的 n 很小,用较差的排序算法也行,例如交换排序。第 3~6 行用交换排序对所有的数 (按字符串处理) 进行排序,复杂度 O(n^2)。

n=int(input())
nums=input().split()    #按字符读入
# print(nums)   
for i in range(0,n-1):  #交换排序
    for j in range(i+1,n):
        if nums[j]+nums[i]>nums[i]+nums[j]:
            nums[j],nums[i]=nums[i],nums[j]
print(''.join(nums))

3、火星人(lanqiaoOJ题号572)

【题目描述】

给出 N 个数的排列,输出这个排列后面的第 M 个排列。

【输入描述】

第一行有一个正整数 N,1<=N<=10000。第二行是一个正整数 M。下一行是 1 到 N 个整数的一个排列,用空格隔开。

【输出描述】

输出一行,这一行含有 N 个整数,表示原排列后面第 M 个排列。每两个相邻的数中间用一个空格分开,不能有多余的空格。

用 Python 编码比较麻烦,因为 Python 的 permutations() 函数是按元素位置进行排列的输出的。只能这样编码:先把 n 个数排序成最小排列,然后从这个最小排列开始 permutations(),遇到题目给定的起始排列后,再往后数到第m个排列,输出。但是这个代码会超时,因为浪费了很多计算。

from itertools import *
from copy import *
n=int(input())
m=int(input())
nums=list(map(str,input().split()))
back=deepcopy(nums)
k=0
flag=0
nums.sort()
for ele in permutations(nums):
    if list(ele)==back:
        flag=1
    if flag==1:
        if k==m:
            a=''.join(ele)
            print(a)
            break   #退出for循环!
        k+=1

一种高效的方法:从当前排列开始,暴力地寻找下一个排列。对于当前排列,从后往前比较,寻找 nums[i-1] < nums[i] 的位置,把 nums[i-1] 与 i 到末尾中比 nums[i-1] 大的最小数交换,再将 i-1 之后的数进行翻转 (从小到大排序),可以得到比当前排列大的最小排列。

n=int(input())
m=int(input())
nums=list(map(int,input().split()))

def find_next(nums):
    for i in range(n-1,0,-1):
        if nums[i]>nums[i-1]:
            for j in range(n-1,i-1,-1):
                if nums[j]>nums[i-1]:
                    nums[j],nums[i-1]=nums[i-1],nums[j]
                    return nums[:i]+nums[:i-1:-1]

for i in range(m):
    nums=find_next(nums)
print(''.join([str(i) for i in nums]))

4、带分数(lanqiaoOJ题号208)

【题目描述】

100 可以表示为带分数的形式:100 = 3 + 69258 / 714。还可以表示为:100 = 82 + 3546 / 197。

注意特征:带分数中,数字 1~9 分别出现且只出现一次(不包含0)。

类似这样的带分数,100 有 11 种表示法。输入一个整数,输出它有多少种表示法。

【输入描述】

从标准输入读入一个正整数 N (N < 1000×1000)。

【输出描述】

程序输出该数字用数码 1~9 不重复不遗漏地组成带分数表示的全部种数。

典型的排列题。题目中说 “数字 1~9 分别出现且只出现一次”,用暴力排列:对所有 1~9 的排列,验证有几个符合要求。9 个数只有 9!=362880 种排列,不会超时。

from itertools import *
n=int(input())
bit=len(str(n))     #n的位数
cnt=0
for num in permutations("123456789"):
    a,b,c=0,0,0
    for a1 in range(bit):   #a1: a的位数,a肯定比n短
        a=int("".join(num[:a1+1]))  #一个a
        bLast=(n-a)*int(num[-1])%10  #b的尾数,(n-a)c%10
        if bLast==0:        #b的尾数不可能等于0,因为只用到1~9
            continue
        b1=num.index(str(bLast))    #根据b的尾数,确定b的长度
        if b1<=a1 or b1>=8:
            continue
        b=int(''.join(num[a1+1:b1+1]))
        c=int(''.join(num[b1+1:]))
        if b%c==0 and n==a+b//c:
            cnt+=1
print(cnt)

以上,蓝桥杯Python组排列和组合、二进制讲解

祝好

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PWA学习】2. 使用 Manifest, 让你的 WebApp 更 Native

引言 我们知道&#xff0c;在 chrome(等一些现代浏览器)中&#xff0c;你可以将访问的网站添加到桌面&#xff0c;这样就会在桌面生成一个类似 “快捷方式” 的图标&#xff0c;当你点击该图标时&#xff0c;便可以快速访问该网站(Web App) 我们以 demo 为例&#xff0c;其添加…

无监督聚类表征学习方法之对比学习(Contrastive Learning)——simclr方法

无监督聚类表征学习方法之对比学习(Contrastive Learning)——simclr方法 1.参考论文 《A Simple Framework for Contrastive Learning of Visual Representations》 2.无监督聚类表征学习方法 主要有几种&#xff1a; ①自动编码器(AutoEncoder,AE); ②变分自编码器(Variatio…

两款开源.NET工作流引擎 Elsa 与ccflow使用比较

相对java开源的工作流程引擎.net开源的工作流程引擎相对较少&#xff0c;这里整理两款.net开源工作流引擎&#xff0c;做一下对比使用。elsa示例代码:Githubd地址&#xff1a;https://github.com/zhenl/MyElsaccflow下载地址&#xff1a;https://gitee.com/opencc/ccflowCCFlow…

Java笔记021-异常-Exception

异常-Exception看个实际问题和一段代码运行下面的代码&#xff0c;看看有什么问题->引出异常和异常处理机制package com12.exception_;/*** author 甲柒* version 1.0* title Exception01* package com12.exception_* time 2023/1/9 14:38*/ public class Exception01 {publ…

Mask RCNN网络源码解读(Ⅳ) --- Mask R-CNN论文解读

目录 1.Mask R-CNN简介 2.Mask分支 3.Mask R-CNN损失 4Mask分支预测使用 1.Mask R-CNN简介 回顾我们之前所说的图像分类、目标检测、语义分割的内容&#xff1a; 我们来看一下实例分割和语义分割的差别&#xff1a; Mask R-CNN不仅能够同时进行目标检测与分割&#xff0c;…

查找算法之二分查找

目录 二分查找 算法实现 “双闭区间”实现 算法实现 python C 两种表示对比 大数越界处理 优点与缺点 二分查找 二分查找&#xff0c;利用数据的有序性&#xff0c;通过每轮缩小一半搜索区间来查找目标元素。 使用二分查找有两个前置条件&#xff1a; 要求输入数据…

如何在GitLab上传本地项目

上传前需准备&#xff1a;需要安装Git&#xff0c;点击进入官网下载&#xff1a;Git 在本地上传GitLab项目的步骤目录介绍&#xff1a; 一、配置SSH秘钥&#xff08;仅针对本机首次上传GitLab项目&#xff09; 二、上传项目 1、新建一个空文件夹&#xff0c;并在该文件夹下右键…

Deque

Deque&#xff1a; “double ended queue&#xff08;双端队列&#xff09;”的缩写&#xff0c;通常读为“deck”&#xff1b; Deque是一个线性集合&#xff0c;支持在两端插入和移除元素。 Deque有三种用途&#xff1a; 双端队列(两端都可进出) Deque< Integer> de…

机器学习实战教程(十三):树回归基础篇

一、前言本篇文章将会讲解CART算法的实现和树的剪枝方法&#xff0c;通过测试不同的数据集&#xff0c;学习CART算法和树剪枝技术。二、将CART&#xff08;Classification And Regression Trees&#xff09;算法用于回归在之前的文章&#xff0c;我们学习了决策树的原理和代码实…

成功上岸字节全靠这份Redis技术笔记,深入浅出值得一看

前言 正如标题所说&#xff0c;我现在已经如愿以偿地进了字节&#xff01;之前自己一直待在一个不大不小的外包公司&#xff0c;每天做着重复的层删改查工作。直到22年年底&#xff0c;自己通过朋友的介绍拿到了字节的面试机会&#xff0c;自己在家复习了3个月&#xff0c;成功…

decltype类型指示符

decltype类型指示符一、什么是decltype类型指示符二、typeid运算符三、使用decltype指示符四、decltype和引用五、decltype(auto)六、本章代码汇总一、什么是decltype类型指示符 有时会遇到这种情况&#xff1a;希望从表达式的类型推断出要定义的变量的类型&#xff0c;但是不…

超实用的实用Shell脚本

一、Dos 攻击防范&#xff08;自动屏蔽攻击 IP&#xff09; 代码&#xff1a; #!/bin/bash DATE$(date %d/%b/%Y:%H:%M) LOG_FILE/usr/local/nginx/logs/demo2.access.log ABNORMAL_IP$(tail -n5000 $LOG_FILE |grep $DATE |awk {a[$1]}END{for(i in a)if(a[i]>10)print…

Spring 学习笔记2

1.spring设置JDBC链接池 classpath:jdbc.properties是有多个连接池时的写法&#xff0c;一般都用这种 还有就是配置文件里不要直接使用username&#xff0c;会被覆盖 使用${}来从文件里读取属性 <beans xmlns"http://www.springframework.org/schema/beans"xmlns…

bitmap原理+性能优化实践

目录 背景 总体结构 从RoaringBitmp说起 3.1arraycontainer 1.3.2 bitmapcontainer 1.3.3 runcontainer 上源码 Roaring64NavigableMap RoaringBitmap RoaringArray 三种Container ArrayContainer BitmapContainer RunContainer 工作应用 需求 分析 能否多线…

ArcGIS基础实验操作100例--实验75气体扩散空间分析

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验75 气体扩散空间分析 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

MySQL常用基础 - 小白必看(二)

MySQL数据库基本操作 一、DDL 概念&#xff1a;是一个数据定义语言 该语言部分包括&#xff1a; 1、对数据库的常用操作 创建数据库&#xff1a; 1、create database 数据库名 (直接删除) 2、create database if not exists 数据库名 &#xff08;判断数据库是否存在&…

视频的水印怎样去掉?这些去水印的方法值得你试试看

喜欢视频剪辑的你会不会经常遇到这种情况&#xff1a;每次上网查找的视频素材&#xff0c;保存下来后总是带有一些水印&#xff0c;这些水印不仅不够美观&#xff0c;而且还会遮挡住视频的一些部分&#xff0c;实在是烦人。如果你遇到这种情况&#xff0c;会很想知道“给视频无…

86、【栈与队列】leetcode ——39. 滑动窗口最大值:单调队列+滑动窗口(C++版本)

题目描述 239. 滑动窗口最大值 一、单调队列滑动窗口方法 本题的特点是维护一个窗口&#xff0c;在窗口不断向前移动时&#xff0c;获取其中的最大值。由于窗口在向前移动过程中&#xff0c;元素存在着进入和出去的连续顺序&#xff0c;与FIFO的特点类似。 故可考虑用队列实…

【数据结构】初识数据结构,十分钟带你玩转算法复杂度

目录 &#x1f34a;前言&#x1f34a;&#xff1a; &#x1f95d;一、初识数据结构&#x1f95d;&#xff1a; 1.数据结构&#xff1a; 2.算法&#xff1a; &#x1f353;二、算法效率&#x1f353;&#xff1a; &#x1f348;三、算法复杂度&#x1f348;&#xff1a; 1.时…

4-1文件管理-文件系统基础

文章目录一.文件的基本概念二.文件的逻辑结构&#xff08;一&#xff09;无结构文件/流式文件&#xff08;二&#xff09;有结构文件1.顺序文件2.索引文件3.索引顺序文件4.直接文件/散列文件三.文件目录四.文件的物理结构/文件分配方式1.连续分配2.链接分配3.索引分配五.文件存…