№①【高手请进】!求组合方案生成算法

donghid 2003-11-24 02:25:13
请高手帮忙写一个函数。
题目要求如下:
从n个元素中每次取i个元素组合所得的方案数为C(n,i)种,
那么由n个元素组合而成的方案总数有
C(n,1)+C(n,2)+……+C(n,n-1)+C(n,n)=S(种)。
现在要求把这S种方案具体构造出来。
示例:
输入:"ABCD"
输出:A,B,C,D;
AB,AC,AD,BC,BD,CD;
ABC,ABD,ACD,BCD;
ABCD;
--------------------------------------------------------
请高手相助,分数不够再加。
...全文
26 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
12s 2003-11-27
  • 打赏
  • 举报
回复
正确!!不好意思!没发觉到!!改改
donghid 2003-11-27
  • 打赏
  • 举报
回复
to 12s(12s):
你这个程序不行的,无法构造出所有的组合。
例如:
输入:ABC
输出结果:
A AB ABC
B BC
C
漏了AC这种情况。


donghid 2003-11-26
  • 打赏
  • 举报
回复
承蒙各位的指点,我自己也写了一个。(javascript版的穷举算法)
<script>
var s="ABCD"
function Com(string){
var len=string.length
for(i=1;i<Math.pow(2,len);i++){
for(j=len-1;j>=0;j--)
if(i&(Math.pow(2,j))) document.write(string.charAt(j))
document.write("<br>")
}
}

Com(s)
</script>

gladiatorcn 2003-11-25
  • 打赏
  • 举报
回复
同意BlueSky2008,这并不是一个NP完全问题。指数算法是无效算法,即使是NP完全,也可以找到相应的启发式算法。
而这其实是一个简单的线性问题。

设有i个输入,则使用一个i位的二进制数作为标示。
这个二进制数的每一位代表一个输入。例如:输入 A,B 则 01表示A 10表示B 11表示AB
for循环2^i次,从1开始。
在写程序时,很容易通过效验二进制位来解决。如上一个例子,if(i&2) *(++s)="B";
如果,输入的可行域过大,即使64位数也解决不了,可以使用数组模拟解决。
PuzzleFan 2003-11-25
  • 打赏
  • 举报
回复

// 让ABCD对应四位二进制数的所有可能,
// 1表示显示对应字母,0表示不显示对应字母
// 1101表示ABD, 0010表示C,

// 结果的第一行显示只有一个1的二进制数对应的字母,
// 结果的第二行显示有两个1的二进制数对应的字母,....

// 外层循环:i 表示有多少个1
// 内层循环:j 遍历 16个数字,count统计每个数有多少个1
// VC6.0 win32 console application 通过


#include "stdafx.h"
#define WIDTH 4 //3 to 6
#define MAX_NUM 16 //8 to 64
char s[30]="ABCDEFGHIJKL";
int main(int argc, char* argv[])
{
int i,j,k,count;
for(i=1; i<=WIDTH;i++){
for(j=0;j<MAX_NUM;j++){
count=0;
for(k=0;k<WIDTH;k++)
if((j>>k) & 0x1)
count++;
if(count==i){
for(k=0;k<WIDTH;k++)
if((j>>k) & 0x1)
putchar(s[k]);
putchar(',');
}
}
printf("\b;\n");
}
getchar();
return(0);
}
12s 2003-11-25
  • 打赏
  • 举报
回复
#include <stdio.h>
#include <stdlib.h>

#define MAX 5

char str[MAX]={'a','b','c','d','e'};

void c(char *string)
{
int i,j,k;
for(i=0;i<MAX;i++) { /*当前打头字符*/
printf("%c\t",string[i]);
for(j=i+1;j<MAX;j++) { /*当前字符数 如:ABC为3个字符*/
printf("%c",string[i]); /*当前开头字母*/
for(k=i+1;k<=j;k++)
printf("%c",string[k]); /*后序字母*/
printf("\t");
}
printf("\n");
}
}

void main()
{
clrscr();
c(str);
getch();
}
短歌如风 2003-11-25
  • 打赏
  • 举报
回复
gladiatorcn:
很明显,你说是线性是因为你把组合数看作N,我们说是指数级是把元素个数看成N。
至于你说的方法(你自己也说要循环2^i次),BlueSky2008已经说过了,而用数组模拟的方法我也给出来了。
PuzzleFan 2003-11-25
  • 打赏
  • 举报
回复
对前面的程序作了一点修改

可以取消数组s[],用putchar('A'+k);替代 putchar(s[k]);
可以取消MAX_NUM,换成max_num


WIDTH不能大于31(受限制于 int 位数)
WIDTH不能大于26(受限制于 26个字母)
WIDTH最好大于20(受限制于 我的CPU)

WIDTH就取个 16 吧


#include "stdafx.h"
#define WIDTH 16
int max_num=1<<WIDTH;
int main(int argc, char* argv[])
{
int i,j,k,count;
for(i=1;i<=WIDTH;i++){
for(j=0;j<max_num;j++){
count=0;
for(k=0;k<WIDTH;k++)
if((j>>k) & 0x1)
count++;
if(count==i){
for(k=0;k<WIDTH;k++)
if((j>>k) & 0x1)
putchar('A'+k);
putchar(',');
}
}
printf("\b;\n");
}
getchar();
return(0);
}
saint001 2003-11-24
  • 打赏
  • 举报
回复
对于一般的字符串,打印输出的时候把组合数字对应到字符串下标就行了
saint001 2003-11-24
  • 打赏
  • 举报
回复
//整理一下
#include "stdio.h"

void print(int *c,int r)
{
int i;
for(i=1;i<=r;i++)
printf("%c",c[i]+'A'-1);
printf("\n");
}

int next(int *c,int n,int r)//计算下一个组合
{
for(int i=r;c[i]>=n-r+i;i--);
if(!i)
return 0;
c[i]++;
for(int j=i+1;j<=r;j++)
c[j]=c[j-1]+1;
return 1;
}

void all(int n,int r)//从n个元素中间取r个的所有组合
{
int i,*c;
c=new int[r+1];
for(i=1;i<=r;i++)
c[i]=i;
print(c,r);
while(next(c,n,r))
print(c,r);
delete c;
}

void all(int n)//输出所有的组合
{
int r;
for(r=1;r<=n;r++)
all(n,r);
}

void main()
{
all(4);
}
saint001 2003-11-24
  • 打赏
  • 举报
回复
#include "stdafx.h"
#include "stdio.h"

void print(int *c,int r)//打印当前组合
{
int i;
for(i=1;i<=r;i++)
printf("%c",c[i]+'A'-1);
printf("\n");
}

int next(int *c,int n,int r)//计算下一个组合
{
for(int i=r;c[i]>=n-r+i;i--);
if(!i)
return 0;
c[i]++;
for(int j=i+1;j<=r;j++)
c[j]=c[j-1]+1;
return 1;
}

void all(int n,int r)//n个元素中间取r个的所有组合
{
int i,*c;
c=new int[r+1];
for(i=1;i<=r;i++)
c[i]=i;
print(c,r);
while(next(c,n,r))
print(c,r);
delete c;
}

void all(int n)//n个元素所有的组合
{
int i,r,*c;
for(r=1;r<=n;r++)
{
c=new int[r+1];
for(i=1;i<=r;i++)
c[i]=i;
print(c,r);
while(next(c,n,r))
print(c,r);
delete c;
}
}

void main()
{
all(4);
}
fjirie 2003-11-24
  • 打赏
  • 举报
回复
//偶的, 星际玩的有点晕了, 写的不是很好~~~~晕中。。。

#include <iostream>
#include <string>
using namespace std;

void Combination_aux(string s, int len) {
if(s.length() >= len) {
if(len==1) {
for(int i=0; i<s.length(); i++)
cout<<s[i]<<endl;
}
else {
for(int j=len-1; j<s.length(); j++) {
for(int i=0; i<len-1; i++)
cout<<s[i];
cout<<s[j]<<endl;
}
Combination_aux(s.substr(1, s.length()-1), len);
}
}
}

void Combination(string s) {
for(int i=1; i<=s.length(); i++)
Combination_aux(s, i);
}

int main()
{
Combination("ABCD");
}
短歌如风 2003-11-24
  • 打赏
  • 举报
回复
用array [0..N-1] of bool表示不同的元素是否出现在组合中:
下面是一个迭代码的版本:

type
TCombination = array of Boolean;
procedure FirstCombination(var Data: TCombination);
var
I: Integer;
begin
for I := 1 to High(Data) do
Data[I] := False;
Data[0] := True;
end;

function NextCombination(var Data: TCombination): Boolean;
var
I: Integer;
begin
I := 0;
while (I < Length(Data)) and (Data[I]) do
begin
Data[I] := False;
Inc(I);
end;
Result := I < Length(Data);
if Result then
Data[I] := True;
end;

var
Combination: TCombination;
begin
SetLength(Combination, N);
FirstCombination(Combination);
repeat
输出Combination
until not NextCombination(Combination);
end;

时间复杂度是O(2^N),正如ZhangYv所说,这是个NP完全问题,无法在多项式的时间复杂度内解决。
kbsoft 2003-11-24
  • 打赏
  • 举报
回复
http://www.csdn.net/Develop/article/22/22071.shtm
ZhangYv 2003-11-24
  • 打赏
  • 举报
回复
上面的程序是取一个普通组合数的,如C(N,M):
#include <stdio.h>
#include <stdlib.h>
const int MAXSIZE = 80;
int N,M;
int A[MAXSIZE], B[MAXSIZE];
void Print()
{
int i;
for (i = 0; i < M; ++i)
printf("%d ", B[i]);
printf("\n");
}

void Com(int n, int m)
{
int i;
for (i = n; i >= m; --i){ //抽屉原理
B[m] = A[i];
if (m > 0)
Com(i-1,m-1);
else
Print();
}
}
int main()
{
int i;
/* printf("%s", "Input N M = ");
scanf("%d%d", &N,&M);
*/
N = 5;
M=3;
if (N > MAXSIZE)
return 1; //越界
for (i = 0; i < N; ++i)
A[i] = i+1;
Com(N-1,M-1);
system("pause");
return 0;
}
下面是求一个集合的所有子集,是NP问题,也就是你要的解答:

一个取集合的所有子集的算法:
void Calcu(int i, const int n, int A[], int B[])
{
if(i>=n){
int j;
for (j = 0; j < n; j++)
if (B[j]) printf("%c",A[j]);
printf("\n");
} else {
B[i] = 1; //取第i个元素
Calcu(i+1, n, A, B);
B[i] = 0; //舍第i个元素
Calcu(i+1, n, A, B);
}
}//集合的幂集问题是NPC的,也就是说规模是指数级别,上述程序效率是O(2^n)
转自:
http://expert.csdn.net/Expert/topic/2059/2059607.xml?temp=.5402033

SoftWare1999 2003-11-24
  • 打赏
  • 举报
回复
穷举啊
比如
a
1/ \0
b b
1/ \0 1/ \0
..........

递规,递推都可以求。
BlueSky2008 2003-11-24
  • 打赏
  • 举报
回复
2^n 种。
用一个整型量从0 变到 2^n-1,用它的二进位表示就行啦。

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧