杭电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;

}

 

posted @ 2015-05-24 16:33  湖心北斗  阅读(290)  评论(0编辑  收藏  举报