排列组合问题

1.排列问题:

(1)递归解法:以abc为例 (1).第一个字符分别与后面的字符交换  (2).第二个字符分别与之后的字符交换,和(1)相同,进行递归。

#include<iostream>
using namespace std;
bool isswap(char *str, int s, int k)
{
	for (int i = s; i < k; i++)
	{
		if (*(str + i) == *(str + k))
			return false;
	}
	return true;
}
void swap(char &t1, char &t2)
{
	char temp = t1;
	t1 = t2;
	t2 = temp;
}
void Permutation(char *str, int k, int m)
{
	if (str == NULL)
		return;
	if (k == m)
	{
		static int n = 0;
		n++;
		cout << n<<"  "<<str << endl;
	}
	else
	{
		for (int i = k; i < m; i++)
		{
			if (isswap(str,k,i))
			{
				swap(*(str + k), *(str + i));
				Permutation(str, k + 1, m);
				swap(*(str + k), *(str + i));
			}
		}
	}
}

int main()
{
	char a[] = "abc";
	int len = sizeof(a) / sizeof(a[0]);
	Permutation(a, 0, len-1);
	getchar();
	return 0;
}


(2)非递归解法:

              1.对初始队列进行排序,找到所有排列中最小的一个排列Pmin。

              2.找到刚刚好比Pmin大比其它都小的排列P(min+1)。

              3.循环执行第二步,直到找到一个最大的排列,算法结束。

           如排列ABCDE,这是所有排列中最小的一个排列,刚好比ABCDE大的排列是:ABCED。

                    算法如下:

                               给定已知序列P =  A1A2A3.....An

                               对P按字典排序,得到P的一个最小排列Pmin =A1A2A3....An ,满足Ai > A(i-1) (1 < i <= n)

                              从Pmin开始,找到刚好比Pmin大的一个排列P(min+1),再找到刚好比P(min+1)大的一个排列,如此重复。

                              1.从后向前(即从An->A1),找到第一对为升序的相邻元素,即Ai < A(i+1)。若找不到这样的Ai,说明已经找到最后一个全排列,可以返回了。

                              2.从后向前,找到第一个比Ai大的数Aj,交换Ai和Aj。

                              3.将排列中A(i+1)A(i+2)....An这个序列的数逆序倒置,即An.....A(i+2)A(i+1)。因为由前面第1、2可以得知,A(i+1)>=A(i+2)>=.....>=An,这为一个升序序列,应将该序列逆序倒置,所得到的新排列才刚刚好比上个排列大。

                              4.重复步骤1-3,直到返回。

#include<iostream>
using namespace std;
int cmp(const void *a,const void *b)
{
	return *(char*)a - *(char*)b;
}
void swap(char *a, char *b)
{
	char temp = *a;
	*a = *b;
	*b = temp;
}
void reverse(char *str, int i,int len)
{
	while (i < len)
	{
		swap(str + i, str + len);
		i++;
		len--;
	}
}
void combination(char *str, int len)
{
	if (len < 1)
		return;
	qsort(str, strlen(str), sizeof(char), cmp);
	int i = 0;
	int j = 0;
	while (true)
	{
		cout << str << endl;
		for (i = len-1; i >= 0; i--)
		{
			if (str[i] < str[i + 1])
				break;
			if (i == 0)
				return;
		}
		for (j = len; j > i; j--)
		{
			if (str[j] > str[i])
				break;
		}
		swap(str[j], str[i]);
		reverse(str, i + 1,len);
	}
}

int main()
{
	char a[] = "bca";
	int len = sizeof(a) / sizeof(a[0]);
	//Permutation(a, 0, len-1);
	combination(a, len - 2);
	getchar();
	return 0;
}


2.组合问题:

(1)递归解法:假设我们想在长度为n的字符串中求m个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。这两种选择都很容易用递归实现

#include<iostream>
#include<vector>
using namespace std;
void combination(char *str, int i, vector<char> &result)
{
	
	if (i == 0)
	{
		vector<char>::iterator iter = result.begin();
		for (; iter != result.end(); iter++)
			cout << *iter;
		cout << endl;
		return;
	}
	if (*str == '\0')
		return;
	result.push_back(*str);
	combination(str + 1, i - 1, result);
	result.pop_back();
	combination(str + 1, i, result);
}
void combination(char *str, int len)
{
	vector<char> result;
	for (int i = 1; i < len; i++)
	{
		combination(str, i, result);
	}
}

int main()
{
	char a[] = "abc";
	int len = sizeof(a) / sizeof(a[0]);
	//Permutation(a, 0, len-1);
	combination(a, len);
	getchar();
	return 0;
}


(2)位运算解法:

#include<iostream>
using namespace std;
void combination(char *str, int i, int len)
{
	for (int j = 0; j < len; j++)
	{
		if (i&(1 << j))
			cout << str[j];
	}
	cout << endl;
}
void combination(char *a, int len)
{
	for (int i = 0; i < (1 << len); i++)
	{
		combination(a, i, len);
	}
}
int main()
{
	char a[] = "abc";
	int len = sizeof(a) / sizeof(a[0]);
	//Permutation(a, 0, len-1);
	combination(a, len-1);
	getchar();
	return 0;
}


3.排列实例(八皇后问题):

基本思想:

由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标i和j,是不是i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]。

#include<iostream>
using namespace std;
void swap(int *a, int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
bool check(int *queue, int queueNumber)
{
	for (int i = 0; i < queueNumber; i++)
		for (int j = i+1; j < queueNumber; j++)
		{
			if (i - j == queue[i] - queue[j] || i - j == queue[j] - queue[i])
				return false;
		}
	return true;
}
void permutation(int *queue, int queueNumber,int begin)
{
	
	if (begin == queueNumber)
	{
		if (check(queue, queueNumber))
		{
			for (int i = 0; i < queueNumber; i++)
				cout << queue[i];
			cout << endl;
		}
	}
	else
	{
		for (int j = begin; j < queueNumber; j++)
		{
			swap(queue[begin], queue[j]);
			permutation(queue, queueNumber, begin + 1);
			swap(queue[begin], queue[j]);
		}
	}
}
void eightQueue()
{
	const int queueNumber = 8;
	int *queue = new int[queueNumber];
	for (int i = 0; i < queueNumber; i++)
	{
		queue[i] = i;
	}
	permutation(queue, queueNumber, 0);
}
int main()
{
	char a[] = "abc";
	int len = sizeof(a) / sizeof(a[0]);
	//Permutation(a, 0, len-1);
	//combination(a, len-1);
	eightQueue();
	getchar();
	return 0;
}


4.组合实例(从1-n中取出和为m的组合):

基本思想:列出所有的组合,判断各种组合的累加和。

#include<iostream>
#include<vector>
using namespace std;
void combination(int *a, int n, int m, int i, vector<int> &result)
{
	if (i == 0)
	{
		int sum = 0;
		vector<int>::iterator iter = result.begin();
		for (; iter != result.end(); iter++)
		{
			sum = sum + *iter;
		}
		if (sum == m)
		{
			iter = result.begin();
			for (; iter != result.end(); iter++)
			{
				cout << *iter<<'\t';
			}
			cout << endl;
		}
		return;
	}
	if (*a == 0)
		return;
	result.push_back(*a);
	combination(a + 1, n, m, i - 1, result);
	result.pop_back();
	combination(a + 1, n, m, i, result);
}
void combination(int n, int m)
{
	int *a =new int[n+1];
	vector<int> result;
	for (int i = 0; i < n; i++)
		a[i] = i + 1;
	a[n] = 0;
	for (int i = 1; i <= n; i++)
		combination(a, n, m,i,result);
	return;

}

int main()
{
	char a[] = "abc";
	int len = sizeof(a) / sizeof(a[0]);
	//Permutation(a, 0, len-1);
	//combination(a, len-1);
	//eightQueue();
	combination(10, 12);
	getchar();
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值