求n个元素的全排列的所有解可以用减治法:每次拎出一个数做前缀,对剩下的元素再求全排列,直至只剩一个元素。代码源自《算法分析与设计(王晓东)》,复杂度O(n!)

 1 //输出k~m的所有全排列
 2 void perm(int k,int m)
 3 {
 4     if(k==m)
 5     {
 6         for(int i=0;i<=m;i++)
 7             printf("%d ", list[i]);
 8         printf("\n");
 9     }else
10     {
11         for(int i=k;i<=m;i++)
12         {
13             swap(list[k],list[i]);
14             perm(k+1,m);
15             swap(list[k],list[i]);
16         }
17     }
18 }

以上没有考虑有重复元素的情况。简单地想,若有元素重复,则这个元素只需被拎出来做一次前缀就好了,那么只需在拎前缀前判断这个数是否已被拎出来过。

那么如何高效地判断呢?可以先对所有元素排序,这样重复元素分布在相邻位置,一趟扫描,只对与前驱不同的元素做处理。

代码只需在k~m的循环内加一句 if(i>k&&list[i]==list[i-1]) continue;

下面再来看一道类似的问题

UVA11076  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=33478

由0~9中n个数字(可重复)组成的数组,把每个全排列看成一个n位数,求所有全排列的和。

根据全排列的性质,将所有全排列按行写出后,发现每一列“所有数字的和s”都相等,因此我们可以只求任一列的和然后进行n次的*10累加。

对于每个数字k,我们已知它在数组中出现的次数cnt[k](即k有cnt[k]-1个副本),但要对一列求和(不妨求第1列),我们需要知道每个数字在这一列出现的次数cnt_2[k]。由于全排列是没有相同的,那么“第1位是k”的次数就等价于“去掉k后剩余元素的全排列的个数”,至此,问题转化为上面的减治法求全排列。

本题只需求全排列个数(值)而不必输出具体排列(解),因此可以用高中排列组合的经典做法:先视为无重复全排,再除以所有重复元素的排列个数。

做完发现此题由于数字是0~9所以天然地把元素排好序并记录好重复次数了,因此对每个数字k只计算一次,计算时将k的个数看作cnt[k]-1即可。

代码如下:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4
 5 typedef unsigned long long ULL;
 6 //只有0~9
 7 ULL fac[]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600};
 8 int a[13];
 9 int cnt[10];//出现的次数
10 int cnt_2[10];//在所有全排列的任一列中出现的次数
11 ULL sum,ans,s;
12 int n;
13
14 int main()
15 {
16     while(scanf("%d",&n)&&n)
17     {
18         memset(cnt,0,sizeof(cnt));
19         sum=0;
20         for(int i=0;i<n;i++)
21         {
22             scanf("%d",&a[i]);
23             sum+=a[i];
24             cnt[a[i]]++;
25         }
26         s=0;
27         for(int i=0;i<=9;i++)
28         {//cnt_2[i]等于去掉一个i后无重复全排列的个数
29             if(cnt[i]==0) continue;
30             cnt_2[i]=fac[n-1];
31             for(int j=0;j<=9;j++)
32             {
33                 if(cnt[i]==0) continue;
34                 if(i==j) cnt_2[i]/=fac[cnt[i]-1];
35                 else cnt_2[i]/=fac[cnt[j]];
36             }
37             s+=i*cnt_2[i];
38         }
39         ans=0;
40         for(int i=0;i<n;i++)
41         {
42             ans+=s;
43             s*=10;
44         }
45         printf("%llu\n",ans);
46     }
47     return 0;
48 }

注:之前自己把自己搞晕过,去重实现不了当成是swap的问题,认为简单交换i与k会破坏“重复元素集中分布”或“有序序列”这两个条件,但再分析发现并没什么关系。。。减治啊减治,前缀被拎走就对后缀的全排列没影响了,只要保证每次循环中重复元素不被拎到同一位置就可以。

从问题本身和算法思想出发去分析还是很有意思的~算法知识博大精深,希望自己多练习多积累,早日不再那么水~~~

减治求有重复元素的全排列相关推荐

  1. 算法学习——求有重复元素的全排列(递归)

    算法学习--求有重复元素的全排列(递归) 思路:看到这个题目首先能想到的一点就是:①我们要求元素的所有全排列②我们要对求出的全排列去重 第一步:求全排列,这里先讨论对不含重复元素的数组元素进行全排列, ...

  2. 九章算法面试题54 带重复元素的全排列

    九章算法官网-原文网址 http://www.jiuzhang.com/problem/54/ 题目 给定一个带重复元素的整数集合,求出这个集合中所有元素的全排列.对于集合[1,1,2],其本质不同的 ...

  3. 【模板】 全排列 有重复元素的全排列

    全排列 #include<bits/stdc++.h> using namespace std; int pl[1001]; void print (int n){for(int i=1; ...

  4. 递归法:求n个元素的全排列

    问题:求n个元素的全排列 举例:ABC 其全排列有ABC ACB BAC BCA CAB CBA 法一: 采用分割的思想把第一个元素和后面的其他元素分开思考将问题简单化 public class Qu ...

  5. 7-1 有重复元素的全排列 (10 分)

    7-1 有重复元素的全排列 (10 分) 计算给定的n个数有多少种排列方式,即求全排列(可能出现重复的元素) 输入格式: 第一行输入数字的数量n(n>2),第二行给出每一个数字. 输出格式: 一 ...

  6. 全排列:不含重复元素和含重复元素的全排列

    1.不含重复元素 算法思路: 1.n个元素全排列 = (n-1)个元素的全排列+(另一个元素作为前缀) 2.出口:如果只有一个元素的全排列,则说明已经排完,输出数组: 3.不断将每个元素放在第一个元素 ...

  7. LeetCode Permutations II(有重复元素的全排列)

    问题:给出一个有重复元素的数组,要求输出全排列 思路:先排序,然后使用next_permutation 具体代码参考: https://github.com/wuli2496/OJ/tree/mast ...

  8. 包含重复元素的全排列

    #include<iostream> #include<fstream> #include<algorithm> using namespace std;int n ...

  9. E. Tyler and Strings(组合计数 + 树状数组/线段树)(带重复元素的全排列)

    题目链接 主要思路还是比较好想的,一些细节公式预处理比较难搞- 参考题解 分析 从前往后遍历,如果s[]剩下的数中,s[i] < t[i]则对答案是有贡献的: 贡献就是后面位置[i+1 ~ n] ...

最新文章

  1. iOS15.4 来袭:新增“男妈妈”表情及口罩面容解锁、AirTags 反跟踪等新功能
  2. one方法报错 select_【总结】Pandas DataFrame 使用方法
  3. 创建java的可执行文件
  4. switch()中不允许的数据类型有?
  5. Python模块之XlsxWriter
  6. VC制作类似于IE4的酷工具条
  7. aspose.cell for java 去水印_【Java编程基本功】(十)输出杨辉三角,输出*号,数组移动...
  8. FireFox的插件
  9. 《FLUENT 14流场分析自学手册》——2.3 FLUENT14.5软件包的安装以及运行
  10. 面试秘籍 | 接口测试常见面试题
  11. 华为静态路由配置案例
  12. 游戏服务器停机维护,游戏服务器如何做到不停机更新
  13. 2020-09-22
  14. 联想笔记本大小写、数字键、触摸板切换图标不显示的解决方案
  15. Boardcast receiver
  16. 计算机子网掩码作用,什么是子网掩码 子网掩码的作用是什么?
  17. 高德地图截屏、标记鼠标跟随示例
  18. WPS无法关闭excel表格,提示:关闭窗口前请先退出编辑单元格内容或格式
  19. 数据库定义语言(DDL)
  20. oracle 表的er图,用PowerDesingner把oracle中的表导成er图

热门文章

  1. 打乱 数字_崔召幼儿园中班悦享时光——亲子益智游戏数字配对
  2. python中astr是啥_python的基本操作
  3. js制定一个单选按钮_【下】每个月整理发票太头疼?手把手教你快速开发一个工具解决!...
  4. 安全模式 提权_记一次渗透挖洞提权实战
  5. 1116: 删除元素
  6. jQuery 遍历:思路总结,项目场景中如何处理/控制获取的 each 遍历次数?
  7. java暂停5s_java如何实现继续/暂停按钮
  8. java读c二进制文件_如何使用JAVA读取C / Matlab创建的二进制文件
  9. es集群搭建_滴滴Elasticsearch 集群跨版本升级与平台重构之路
  10. android 图片 切换,Android 应用开发笔记 - 切换图片(ImageSwitcher)