杭电acm 排列2

题目地址:http://acm.hdu.edu.cn/game/entry/problem/show.php?chapterid=1&sectionid=3&problemid=17

 

这题的核心算法就是排列问题:

就目前常用的排列算法有两种:

一种是按字典列出排序,C++ STL所使用的方法,能够支持重复元素的全排列。

另外一种是使用递归生成排序。

先说容易理解的一种方法,使用递归生成排序:

例如1,2,3,4,这个序列。

最开始,也就是递归最外层我们可以将其分成:

1    2,3,4,

2    1,3,4

3    1,2,4

4    1,2,3

这四个子分组:

然后递归到某一个子分组当中比如2,3,4

于是就可以得到:

2  3,4

3  2,4

4  2,3

这三个子分组。

再次递归进入某一个分组:

3    4

4    3

当只有第一个数的时候,便不可分了,打印该排列。

于是递归的算法可以写成:

perm(int *list,int l,int r){

    if(l>r){//递归结束条件

        for(int i=0;i<4;i++){

            printf("%d ",list[i]);

        }

        break;

    }else{

        for(int i=l;i<=r;i++){

            swap(&list[i],&list[l]);//传递引用,否则交换失败

            perm(list,l+1,r);

            swap(&list[i],&list[l]);//交换回来,保证原数组不变

        }

    }

}

按照字典列出排序的方法:

它的思想就是不断的找到一个排列中的下一个排列,而且这两个排列之间不会再有任何排列,整个排列都是从小到大排序的,

比如一个任意排列:134987652,我们如何确定下一个排列是多少呢?

从右往左看,第一个小于它的右边的数是4,,13(4)987652。

然后从4右边的数字开始,我们找到最后一个大于它的数字5,1349876(5)2.。

然后调换4,5,得到数字串:13(5)9876(4)2.

翻转5 后面的序列我们得到:13(5)2(4)6789

我们获得的这个序列就是134987652的下一个排列的数字。

为什么呢?

我们仔细研究就会发现:

  1. 在第一步从右往左寻找第一个比他右边小的数字的时候,就是确定最后一个递减子序列的位置,直接将其翻转就是该子序列的字典最小值,我们在翻转之前需要变化递减子序列的前一个数字也就是4,[13(4)987652]。

  2. 确定该子序列的前一个数,我们将递减子序列当中的大于该数的最小数与它交换,在例子中也就是4和5交换,13(5)9876(4)2,于是我们可以知道,调换以后,递减子序列9876(4)2依然保持递减的特性,而递减子序列之前的数字增大,递减子序列之前的序列13(5)在字典序上恰好增大到下一个字典子序列,这时,我们需要递减子序列的最小字典子序列。

  3. 我们翻转递减子序列,得到递减子序列的最小字典序987652,于是就得到了整个序列的递增的下一个字典序列。

字典实现排列算法的代码如下:

bool next_perm(int *list,int l,int r){

	int k=r;

	while(k>l&&list[k-1]>=list[k])k--;

	if(k==l){

		//已经不存在递增部分,此时已经到达最后一个数字。

		return false;

	}

	int j=k;//取 k-1

	while(j<=r&&list[j]>list[k-1])j++;

	//取j-1

	swap(list[k-1],list[j-1]);//交换list[k-1],list[j-1]

    inverse(list,k,r);

    for(int i=l;i<=r;i++){

    	printf("%d ",list[i]);

    }

    printf("\n");

    return true;

}

说了这么多,回到排列2这道题目上,我的ac代码如下:

#include<iostream>



#include<cstdio>



#include<cstdlib>



#include<algorithm>



#include<set>



using namespace std;





set<int> s;





int cnt=0;



void init(){



cnt=0;



s.clear();



}





void swap(int *a,int *b){



int m=*a;



*a = *b;



*b = m;



}



//递归算法



void perm(int *list, int l,int r){



if(l>r){



int v=0;



for(int i=0;i<4;i++){



v=v*10+list[i];



}



s.insert(v);



}else{



for(int i=l;i<=r;i++){



swap(&list[l],&list[i]);



perm(list,l+1,r);



swap(&list[l],&list[i]);



}



}





}



int main(){



int list[4];



bool first=true;



while(scanf("%d %d %d %d",&list[0],&list[1],&list[2],&list[3])==4){



if(list[0]==0&&list[1]==0&&list[2]==0&&list[3]==0){



break;



}



   if(!first)printf("\n");



   first = false;



   init();



perm(list,0,3);



set<int>::iterator iter;



int header = -1;



for(iter=s.begin();iter!=s.end();iter++){



int value = *iter;



if(value>=1000){



if(header==-1){



header = value/1000;



printf("%d",value);



continue;



}



if(header==value/1000){



printf(" %d",value);



continue;



}



if(header!=value/1000&&header!=-1){



header = value/1000;



printf("\n%d",value);



continue;



}



}



}



printf("\n");



}



return 0;



}

 

你可能感兴趣的:(ACM)