#include "iostream"
using namespace std;
/*
功能:求集合的子集
描述:求一个集合的所有子集,如集合{1,2,3},则它的子集为
{},{1},{2},{1,2},{3},{1,3},{2,3},{1,2,3}。
解决:集合的所有子集的个数为2^n,时间复杂度为2^n,空间复杂度做到n,
用二进制来表示子集。此算法为优解,不重复一个也不漏掉一个。
详细:比如有集合{1,2,3},则子集对应的二进制为:000, 100,010,110,001,101,011,111。
取出位为1对应的数:000-->{}, 100-->{1}, 010-->{2}, 110-->{1,2}, 001-->{3}, 101-->{1,3},
011-->{2,3}, 111-->{1,2,3}。
缺陷:从000到111我们用位移<<的方式来表示,但若这个集合很大,超过64了就不能用位移了,
这样的话我们就自定义的一个跟集合一样大的数组,值为0或者1,来模拟二进制。
扩展:求所有子集元素的和为SUM的子集。
优化:模拟“<<”操作符的MoveToNext函数待以后优化。
版本:VS1.0 Create By Duz on 2015.7.7
*/
/* 集合大小200 */
#define NUM 200
/* 子集元素和为10的子集 */
#define SUM 10
/* 需要的子集个数 */
#define GATHER_NUM 5
//数据入口
void init(int *a);
void init(int *a, char *filename){}
//判断是否为最后一个子集
bool isEnd(int *a);
//模拟位移<<1,这个算法唯一可优化的地方就在于这个函数,留后续空余时间优化。
void MoveToNext(int *a);
void print(int *a);
int main(int argc, char **argv)
{
int a[NUM] = {0}; //集合
int index[NUM] = {0}; //集合对应的位
int sum = 0; //子集的和
int resultNum = 0; //得到的子集的个数
//读入集合数据
init(a);
while (1){
sum = 0;
//移位
MoveToNext(index);
//得到子集的和
for (int i = 0; i < NUM; i++){
if (index[i]){
sum += a[i];
}
}
//如果子集和为所需,则输出结果。
if (SUM == sum){
resultNum++;
for (int i = 0; i < NUM; i++){
if (index[i]){
cout << a[i] << " ,";
}
}
cout << endl;
}
//得到了所需的子集个数,退出
if (GATHER_NUM == resultNum){
break;
}
//所有的子集都取完了
if (isEnd(index)){
break;
}
}
system("pause");
return 0;
}
void MoveToNext(int *a)
{
for (int i = 0; i < NUM; i++){
if (0 == *(a + i)){ //1,找到第一个为0的位
//2,将第一个为0的为置为1
*(a + i) = 1;
//3,之前的位全部置为0
memset(a, 0x00, i * sizeof(int));
break;
}
}
}
bool isEnd(int *a)
{
for (int i = 0; i < NUM; i++){
if (0 == *(a + i)){
return false;
}
}
return true;
}
void init(int *a)
{
for (int i = 0; i < NUM; i++){
*(a + i) = rand() % 10;
}
}
void print(int *a)
{
for (int i = 0; i < NUM; i++){
cout << *(a + i) << ",";
}
cout << endl;
}
求集合的子集
最新推荐文章于 2024-03-23 21:30:16 发布