• 【算法学习笔记】13.暴力求解法01 枚举排列


    所谓暴力求解法,大意应该是根据提议分析出的所有可能的情况(优化过的研究域)来一一研究,得到最终结果

    第一节呢就是枚举排列

    这里的排列指的是一个集合的元素中根据字典序进行排列。用wikioi的一道题来进行解释吧

    样例输入 Sample Input
    3
    样例输出 Sample Output
    1 2 3
    1 3 2
    2 1 3
    2 3 1
    3 1 2
    3 2 1 

    此排列的生成过程可以认为是 第一位从1开始进行, 而第二位从可能的最小的数开始遍历,第三位又是在第二位的基础上从可能的最小的数上继续变化。

    这里说白了就是一个递归过程。

    1.得到已经排完的序列a,以及cur表示当前进行到的位置(从0开始计数)

    2.从此序列中得到可能的数(不重复),从其中进行下一次排列

    void pailie(int* a,int cur)
    {
    	int i,j,k,ok;
    	//如果cur == n 表面 a已经到了尽头 开始打印序列 (递归边界)
    	if(cur==n)	
    	{
    		for(k=0;i<n;i++) 
    			printf("%d ",a[i]);//printf 的效率比cout快不少呢
    		printf("
    ");
    		return;
    	} 
    	for(i=1;i<=n;i++)
    	{ 
    		ok=1;//设定标志变量
    		for(j=0;j<cur;j++)	if(a[j]==i)	
    		{
    			ok=0;	break;//找到了一个还没有在a中出现的最小的元素用来打头
    		} 
    		if(ok)
    		{
    			a[cur]=i;//打头
    			pailie(a,cur+1);//打头之后后面继续走,此时的已排好的序列中已经包含了i 光标也前进了一位
    		}
    	}
    		
    }
    
    之后调用pailie(ans,0);即可
    这里用递归来处理是非常好的选择,STL库也提供了一个方法,就是用不断根据上一个序列生成下一个序列的思维方式来处理问题

    首先我们要获得一个最字典序的排列,用sort函数就可以了

    然后用do while循环来调用next_permutation 这样的好处是哪怕只有一个排列也可以处理了、

    核心代码如下


    #include <algorithm>
    <span style="white-space:pre">	</span>int p[4]={1,4,2,2};
    	int n=4;
    	
    	sort(p,p+n);//不知道内部实现到底是什么 = =  凑合用吧stl应该不会坑人
    	do
    	{
    		for(int i=0;i<n;i++) 
    			printf("%d ",p[i]);
    		printf("
    ");	
    	}while(next_permutation(p,p+n));

    下面来说说怎么处理可重复的排列。(书上说next_permutation可以用来处理重复序列可是我发现不好使呀~ 不知道那里设置错了,以后再来解决) 

    已经解决,原年是对重复排列的理解出现了错误

    那么就在递归的方法上进行加工。

    in

    4
    1 2 3 2

    out
    1 2 2 3
    1 2 3 2
    1 3 2 2
    2 1 2 3
    2 1 3 2
    2 2 1 3
    2 2 3 1
    2 3 1 2
    2 3 2 1
    3 1 2 2
    3 2 1 2
    3 2 2 1  

    这里我们就要考虑几个细节问题。

    比如 如何区分重复了的元素,使得排列数量保持正确 这就需要我们记录某一种元素在已经拍完的序列A中出现的次数c1,和总集合的中的个数c2

    只要c1<c2 说明还有可以排的元素,所以要继续递归。


    void pailie(int* a,int cur) 
    {
    	int k;
    	//如果cur == n 表面 a已经到了尽头
    	if(cur==n)	
    	{
    		for(int i=0;i<n;i++) 	printf("%d ",a[i]);
    		putchar('
    ');
    		return;
    	} 
    	for(int i=0;i<n;i++)//如果是i=0即第一个元素 则直接进入处理 因为他肯定没有被重复,如果是第2个开始则需要进行筛选
    	if(!i||p[i]!=p[i-1]){ 
    		int c1 = 0, c2 = 0;
    	    for(int j=0; j<cur; j++) if(a[j] == p[i]) c1++;
    	    for(int j=0; j<n; j++) if(p[j] == p[i]) c2++;
    	    if(c1 < c2) {
    	      a[cur] = p[i];//将元素充入排列中 
    	      pailie2(a, cur+1);
    	    }
    	} 
    }


    总体上大概就是如此



  • 相关阅读:
    bzoj 3456 城市规划 —— 分治FFT / 多项式求逆 / 指数型生成函数(多项式求ln)
    洛谷 P4721 [模板]分治FFT —— 分治FFT / 多项式求逆
    CF 438 E & bzoj 3625 小朋友和二叉树 —— 多项式开方
    Codeforces 447
    Codeforces 1099
    Codeforces 991
    Codeforces 994
    Codeforces 989
    Codeforces 1084
    xj膜你赛(n-1)
  • 原文地址:https://www.cnblogs.com/yuchenlin/p/4379261.html
Copyright © 2020-2023  润新知