减治求有重复元素的全排列

求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会破坏“重复元素集中分布”或“有序序列”这两个条件,但再分析发现并没什么关系。。。减治啊减治,前缀被拎走就对后缀的全排列没影响了,只要保证每次循环中重复元素不被拎到同一位置就可以。

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

时间: 2024-10-26 14:54:08

减治求有重复元素的全排列的相关文章

求数组元素的全排列,数组不含重复元素

Permutations Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the following permutations:     [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1]. 求数组元素的全排列,数组不含重复元素 算法1:递归 类似于DFS的递归. 对于包含n个元素的数组,先确定第一位置

c语言-关于去除数组中重复元素的问题

问题描述 关于去除数组中重复元素的问题 源代码:#include #include int main() { int *a; int n,i,j; scanf("%d",n); a=(int *)malloc(n*sizeof(int)); for (i=0;i<n;i++) scanf("%d",&a[i]); for (i=0;i<n;i++) for (j=1;j<n;j++) if (a[i]==a[j]) printf (&quo

arraylist-ArrayList实现基本的去除重复元素功能

问题描述 ArrayList实现基本的去除重复元素功能 这是我做的程序,想实现一个消除重复元素的功能,但是出现了问题,求帮助 下面是错误提示: 多谢! 解决方案 强烈建议你的类名改成其他的 解决方案二: Java编程:定义功能去除ArrayList中的重复元素黑马程序员_练习:定义功能去除ArrayList中的重复元素---------------------- 解决方案三: lz能否把代码放在代码片段里面再发一次啊... 解决方案四: TreeSet能够排序且保持元素的唯一性, 可以考虑用Tr

[经典面试题][谷歌]一个大小为n的数组,里面的数都属于范围[0, n-1],有不确定的重复元素,找到至少一个重复元素

题目 一个大小为n的数组,里面的数都属于范围[0, n-1],有不确定的重复元素,找到至少一个重复元素,要求O(1)空间和O(n)时间. 思路一 寻找重复元素,很容易想到建立哈希表来完成,遍历一遍数组就可以将每个元素映射到哈希表中.如果哈希表中已经存在这个元素则说明这就是个重复元素.这种方法可以很方便的在O(n)时间内完成对重复元素的查找.可是题目要求在O(1)的空间.因此采用哈希表这种解法肯定在空间复杂度上是不符合要求的.题目中数组中所以数字都在[0, n-1]区间范围内,因此哈希表的大小为n

javascript删除数组重复元素的方法汇总

  本文实例讲述了javascript删除数组重复元素的方法.分享给大家供大家参考.具体分析如下: 这里分享一个前端面试高频题,主要实现javascript删除数组重复元素.希望对初学者有所帮助 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //数组去重的方法 Array.prototype.unique=function(){ //集中声明变量 var oldArr=this, newArr=[oldArr[0]], len=oldA

php从数组中随机选择若干不重复元素的方法

 本文实例讲述了php从数组中随机选择若干不重复元素的方法.分享给大家供大家参考.具体实现方法如下:   代码如下: <?php /*  * $array = the array to be filtered  * $total = the maximum number of items to return  * $unique = whether or not to remove duplicates before getting a random list  */ function uniq

js 高效去除数组重复元素示例代码

 有关使用js去除数组重复元素的文章在之前也有介绍过,下面有个不错示例,感兴趣的朋友可以参考下 代码如下: function unique(data){  data = data || [];  var a = {};  for (var i=0; i<data.length; i++) {  var v = data[i];  if (typeof(a[v]) == 'undefined'){  a[v] = 1;  }  };  data.length=0;  for (var i in a

js 处理数组重复元素示例代码

 数组重复元素如何处理,这是在项目实战中经常遇到的,针对这个问题,下文给出详细解决方法,希望对大家有所帮助 代码如下: function doSz{    var kzly==["a","a","c","a","b"];    for(var i=0;i<kzly.length;i++){  var targetNode = kzly[i];  for (var j=0; j<i; j++) {

JavaScript移除数组内重复元素的方法

 这篇文章主要介绍了JavaScript移除数组内重复元素的方法,实例分析了javascript遍历数组及删除等操作的相关技巧,需要的朋友可以参考下     本文实例讲述了JavaScript移除数组内重复元素的方法.分享给大家供大家参考.具体分析如下: 这段JS代码用于从数组中移除重复的元素,比如: ['apple', 'orange', 'peach', 'apple', 'strawberry', 'orange'] 去重后返回:s ['apple', 'orange', 'peach',