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

}
时间: 2024-11-01 18:30:06

杭电acm 排列2的相关文章

杭电ACM分类

杭电ACM分类: 1001 整数求和 水题1002 C语言实验题——两个数比较 水题1003 1.2.3.4.5... 简单题1004 渊子赛马 排序+贪心的方法归并1005 Hero In Maze 广度搜索1006 Redraiment猜想 数论:容斥定理1007 童年生活二三事 递推题1008 University 简单hash1009 目标柏林 简单模拟题1010 Rails 模拟题(堆栈)1011 Box of Bricks 简单题1012 IMMEDIATE DECODABILITY

杭电ACM水仙花数

水仙花数 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 96473    Accepted Submission(s): 28632 Problem Description 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的: "水仙花数"是指一个三位数,它的各位数字的立方和等于其本身,比如:1

杭电ACM Java实现样例

若使用Java求解杭电ACM,答案提交必须注意两点: 1.类名一定得是Main,否则服务器无法编译: 2.必须用while进行输入判断. 以1000题测试题为例,代码如下: import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scan=new Scanner(System.in); while(scan.hasNextInt()) { int a=scan.n

杭电ACM题目分类

杭电ACM题目分类 基础题:1000.1001.1004.1005.1008.1012.1013.1014.1017.1019.1021.1028. 1029.1032.1037.1040.1048.1056.1058.1061.1070.1076.1089.1090.1091.1092. 1093.1094.1095.1096.1097.1098.1106.1108.1157.1163.1164.1170.1194.1196. 1197.1201.1202.1205.1219.1234.123

杭电 acm 2053 ( Switch Game )

这题思路: 一开始有n盏灯,且全部为关闭状态,都记为 0  就是  The initial condition :      0 0 0 0 0 … 然后之后进行i操作就是对这些灯以是否能被i整除,进行改变状态,如将 0 改为 1 或 将 1 改为 0 正如提醒里的 After the first operation :  1 1 1 1 1 … After the second operation : 1 0 1 0 1 … After the third operation :  1 0 0

杭电acm 1034题

Problem Description A number of students sit in a circle facing their teacher in the center. Each student initially has an even number of pieces of candy. When the teacher blows a whistle, each student simultaneously gives half of his or her candy to

杭电acm 2095 find your present (2)

#include<iostream>using namespace std;int main(){    int n,x,y;    while(cin>>n,n)    {         cin>>x;                   while(--n)         {             cin>>y;             x=x^y;         }         cout<<x<<"\n&q

杭电ACM 2046 阿牛的EOF牛肉串

我用到了两个数组,d1[n]表示长度为n的牛肉串最后一个字符不是'O',d2[n]表示长度为n的牛肉串最后一个字符是'O'.这样结果就是d1[n]+d2[n]:对于已经得到了长度为n-1的牛肉串,我们可以来讨论在第n个位置放置何种字符的牛肉串. 已得到第n-1个位置的字符 第n个位置需要放置的字符 结果 不是'O' 不是'O' 得到长度为n的,结尾不是'O'的字符串 不是'O' 是'O' 得到长度为n的,结尾是'O'的字符串 是'O' 不是'O' 得到长度为n的,结尾不是'O'的字符串 是'O'

杭电acm 1049题

一道水题..... 大意是一条1inch的虫子在一个n inch的盒子的底部,有足够的能够每一分钟往上爬u inch,但是需要休息一分钟,这期间会往下掉d inch,虫子爬到盒子口即认为结束.要求计算出给定的n,u,d虫子爬上的时间. 1 /****************************************************** 2 杭电acm 1049题 已AC 3 *****************************************************/