27.Algorithm Gossip: 排列组合

说明

将一组数字、字母或符号进行排列,以得到不同的组合顺序,例如1 2 3这三个数的排列组合有:1 2 3、1 3 2、2 1 3、2 3 1、3 1 2、3 2 1。

解法

可以使用递回将问题切割为较小的单元进行排列组合,例如1 2 3 4的排列可以分为1 [2 3 4]、2 [1 3 4]、3 [1 2 4]、4 [1 2 3]进行排列,这边利用旋转法,先将旋转间隔设为0,将最右边的数字旋转至最左边,并逐步增加旋转的间隔,例如:

1 2 3 4 -> 旋转1 -> 继续将右边2 3 4进行递回处理

2 1 3 4 -> 旋转1 2 变为 2 1-> 继续将右边1 3 4进行递回处理

3 1 2 4 -> 旋转1 2 3变为 3 1 2 -> 继续将右边1 2 4进行递回处理

4 1 2 3 -> 旋转1 2 3 4变为4 1 2 3 -> 继续将右边1 2 3进行递回处理

#include <stdio.h>
#include <stdlib.h>
#define N 4
void perm(int*, int);
int main(void) { int num[N+1], i; for(i = 1; i <= N; i++) num[i] = i; perm(num, 1); return 0;
}
void perm(int* num, int i) { int j, k, tmp; if(i < N) { for(j = i; j <= N; j++) { tmp = num[j]; // 旋转该区段最右边数字至最左边for(k = j; k > i; k--) num[k] = num[k-1]; num[i] = tmp; perm(num, i+1); // 还原for(k = i; k < j; k++) num[k] = num[k+1]; num[j] = tmp; } } else { // 显示此次排列for(j = 1; j <= N; j++) printf("%d ", num[j]); printf("\n"); }
}

28.Algorithm Gossip: 格雷码(Gray Code)

说明

Gray Code是一个数列集合,每个数使用二进位来表示,假设使用n位来表示每个数好了,任两个数之间只有一个位值不同,例如以下为3位的Gray Code:

000 001 011 010 110 111 101 100

由定义可以知道,Gray Code的顺序并不是唯一的,例如将上面的数列反过来写,也是一组Gray Code:

100 101 111 110 010 011 001 000

Gray Code是由贝尔实验室的Frank Gray在1940年代提出的,用来在使用PCM(Pusle Code Modulation)方法传送讯号时避免出错,并于1953年三月十七日取得美国专利。

解法

由于Gray Code相邻两数之间只改变一个位,所以可观察Gray Code从1变0或从0变1时的位置,假设有4位的Gray Code如下:

0000 0001 0011 0010 0110 0111 0101 0100

1100 1101 1111 1110 1010 1011 1001 1000

观察奇数项的变化时,我们发现无论它是第几个Gray Code,永远只改变最右边的位,如果是1就改为0,如果是0就改为1。

观察偶数项的变化时,我们发现所改变的位,是由右边算来第一个1的左边位。

以上两个变化规则是固定的,无论位数为何;所以只要判断位的位置是奇数还是偶数,就可以决定要改变哪一个位的值,为了程序撰写方便,将阵列索引 0当作最右边的值,而在打印结果时,是由索引数字大的开始反向打印。

将2位的Gray Code当作平面座标来看,可以构成一个四边形,您可以发现从任一顶点出发,绕四边形周长绕一圈,所经过的顶点座标就是一组Gray Code,所以您可以得到四组Gray Code。

同样的将3位的Gray Code当作平面座标来看的话,可以构成一个正立方体,如果您可以从任一顶点出发,将所有的边长走过,并不重复经过顶点的话,所经过的顶点座标顺序之组合也就是一组Gray Code。

#include <stdio.h>
#include <stdlib.h>
#define MAXBIT 20
#define TRUE 1
#define CHANGE_BIT(x) x = ((x) == '0' ? '1' : '0')
#define NEXT(x) x = (1 - (x))
int main(void) { char digit[MAXBIT]; int i, bits, odd; printf("输入位数:"); scanf("%d", &bits); for(i = 0; i < bits; i++) { digit[i] = '0'; printf("0"); } printf("\n"); odd = TRUE; while(1) { if(odd) CHANGE_BIT(digit[0]); else { // 计算第一个1的位置for(i = 0; i < bits && digit[i] == '0'; i++) ; if(i == bits - 1) // 最后一个Gray Code break; CHANGE_BIT(digit[i+1]); } for(i = bits - 1; i >= 0; i--) printf("%c", digit[i]); printf("\n"); NEXT(odd); } return 0;
}

29.Algorithm Gossip: 产生可能的集合

说明

给定一组数字或符号,产生所有可能的集合(包括空集合),例如给定1 2 3,则可能的集合为:{}、{1}、{1,2}、{1,2,3}、{1,3}、{2}、{2,3}、{3}。

解法

如果不考虑字典顺序,则有个简单的方法可以产生所有的集合,思考二进位数字加法,并注意1出现的位置,如果每个位置都对应一个数字,则由1所对应的数字所产生的就是一个集合,例如:

000 {}

001 {3}

010 {2}

011 {2,3}

100 {1}

101 {1,3}

110 {1,2}

111 {1,2,3}

了解这个方法之后,剩下的就是如何产生二进位数?有许多方法可以使用,您可以使用unsigned型别加上&位运算来产生,这边则是使用阵列搜寻,首先阵列内容全为0,找第一个1,在还没找到之前将走访过的内容变为0,而第一个找到的0则变为1,如此重复直到所有的阵列元素都变为1为止,例如:

000 => 100 => 010 => 110 => 001 => 101 => 011 => 111

如果要产生字典顺序,例如若有4个元素,则:

{} => {1} => {1,2} => {1,2,3} => {1,2,3,4} =>

{1,2,4} =>

{1,3} => {1,3,4} =>

{1,4} =>

{2} => {2,3} => {2,3,4} =>

{2,4} =>

{3} => {3,4} =>

{4}

简单的说,如果有n个元素要产生可能的集合,当依序产生集合时,如果最后一个元素是n,而倒数第二个元素是m的话,例如:

{a b c d e n}

则下一个集合就是{a b c d e+1},再依序加入后续的元素。

例如有四个元素,而当产生{1 2 3 4}集合时,则下一个集合就是{1 2 3+1},也就是{1 2 4},由于最后一个元素还是4,所以下一个集合就是{1 2+1},也就是{1 3},接下来再加入后续元素4,也就是{1 3 4},由于又遇到元素4,所以下一个集合是{1 3+1},也就是{1 4}。

C(无字典顺序)

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
int main(void) { char digit[MAXSIZE]; int i, j; int n; printf("输入集合个数:"); scanf("%d", &n); for(i = 0; i < n; i++) digit[i] = '0'; printf("\n{}"); // 空集合while(1) { // 找第一个0,并将找到前所经过的元素变为0 for(i = 0; i < n && digit[i] == '1'; digit[i] = '0', i++); if(i == n) // 找不到0 break; else // 将第一个找到的0变为1 digit[i] = '1'; // 找第一个1,并记录对应位置for(i = 0; i < n && digit[i] == '0'; i++); printf("\n{%d", i+1); for(j = i + 1; j < n; j++) if(digit[j] == '1') printf(",%d", j + 1); printf("}"); } printf("\n"); return 0;
}

C(字典顺序)

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
int main(void) { int set[MAXSIZE]; int i, n, position = 0; printf("输入集合个数:"); scanf("%d", &n); printf("\n{}"); set[position] = 1; while(1) { printf("\n{%d", set[0]); // 印第一个数for(i = 1; i <= position; i++) printf(",%d", set[i]); printf("}"); if(set[position] < n) { // 递增集合个数set[position+1] = set[position] + 1; position++; } else if(position != 0) { // 如果不是第一个位置position--; // 倒退set[position]++; // 下一个集合尾数
        } else // 已倒退至第一个位置break; } printf("\n"); return 0;
}

对应的C++代码:

非字典序:

#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){int n=4;string s(n,'0');int count=0;cout<<++count<<' '<<s<<endl;while(1){int i=0;//找到第一个0,并将找到前所经过的元素变为0while(i<n&&s[i]=='1') {s[i]='0';i++;}if(i==n)//没有找到0,说明已经完毕break;else//将第一个找到的0变为1s[i]='1';cout<<++count<<' '<<s<<endl;}
}

字典序:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){int n=4;vector<int> vr(n);int pos=0;vr[pos]=1;while(1){cout<<"{"<<vr[0];//输出第一个for(int i=1;i<=pos;i++)cout<<","<<vr[i];cout<<"}";if(vr[pos]<n){//递增集合个数vr[pos+1]=vr[pos]+1;pos++;}else if(pos!=0){ // 如果不是第一个位置pos--;// 倒退vr[pos]++; // 下一个集合尾数
        }else // 已倒退至第一个位置break;}
}

30.Algorithm Gossip: m元素集合的n个元素子集

说明

假设有个集合拥有m个元素,任意的从集合中取出n个元素,则这n个元素所形成的可能子集有那些?

解法

假设有5个元素的集点,取出3个元素的可能子集如下:

{1 2 3}、{1 2 4 }、{1 2 5}、{1 3 4}、{1 3 5}、{1 4 5}、{2 3 4}、{2 3 5}、{2 4 5}、{3 4 5}

这些子集已经使用字典顺序排列,如此才可以观察出一些规则:

如果最右一个元素小于m,则如同码表一样的不断加1

如果右边一位已至最大值,则加1的位置往左移

每次加1的位置往左移后,必须重新调整右边的元素为递减顺序

所以关键点就在于哪一个位置必须进行加1的动作,到底是最右一个位置要加1?还是其它的位置?

在实际撰写程序时,可以使用一个变数positon来记录加1的位置,position的初值设定为n-1,因为我们要使用阵列,而最右边的索引值为最大 的n-1,在position位置的值若小于m就不断加1,如果大于m了,position就减1,也就是往左移一个位置;由于位置左移后,右边的元素会 经过调整,所以我们必须检查最右边的元素是否小于m,如果是,则position调整回n-1,如果不是,则positon维持不变。

实作

#include <stdio.h>
#include <stdlib.h>
#define MAX 20
int main(void) { int set[MAX]; int m, n, position; int i; printf("输入集合个数 m:"); scanf("%d", &m); printf("输入取出元素 n:"); scanf("%d", &n); for(i = 0; i < n; i++) set[i] = i + 1; // 显示第一个集合for(i = 0; i < n; i++) printf("%d ", set[i]); putchar('\n'); position = n - 1; while(1) { if(set[n-1] == m) position--; else position = n - 1; set[position]++; // 调整右边元素for(i = position + 1; i < n; i++) set[i] = set[i-1] + 1; for(i = 0; i < n; i++) printf("%d ", set[i]); putchar('\n'); if(set[0] >= m - n + 1) break; } return 0;
}

C++代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){int n=3;int m=6;vector<int> vr(n);for(int i=0;i<n;i++)vr[i]=i+1;cout<<"{";copy(vr.begin(),vr.end(),ostream_iterator<int>(cout," "));cout<<"}";int pos=n-1;while(1){if(vr[n-1]==m)pos--;elsepos=n-1;vr[pos]++;for(int i=pos+1;i<n;i++)vr[i]=vr[i-1]+1;cout<<"{";copy(vr.begin(),vr.end(),ostream_iterator<int>(cout," "));cout<<"}";if(vr[0]>=m-n+1)break;}
}

31.Algorithm Gossip: 数字拆解

题目是这样的:

3 = 2+1 = 1+1+1 所以3有三种拆法

4 = 3 + 1 = 2 + 2 = 2 + 1 + 1 = 1 + 1 + 1 + 1 共五种

5 = 4 + 1 = 3 + 2 = 3 + 1 + 1 = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 +1 +1 +1 共七种

依此类推,请问一个指定数字NUM的拆解方法个数有多少个?

解法

我们以上例中最后一个数字5的拆解为例,假设f( n )为数字n的可拆解方式个数,而f(x, y)为使用y以下的数字来拆解x的方法个数,则观察:

5 = 4 + 1 = 3 + 2 = 3 + 1 + 1 = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 +1 +1 +1

使用函式来表示的话:

f(5) = f(4, 1) + f(3,2) + f(2,3) + f(1,4) + f(0,5)

其中f(1, 4) = f(1, 3) + f(1, 2) + f(1, 1),但是使用大于1的数字来拆解1没有意义,所以f(1, 4) = f(1, 1),而同样的,f(0, 5)会等于f(0, 0),所以:

f(5) = f(4, 1) + f(3,2) + f(2,3) + f(1,1) + f(0,0)

依照以上的说明,使用动态程序规画(Dynamic programming)来进行求解,其中f(4,1)其实就是f(5-1, min(5-1,1)),f(x, y)就等于f(n-y, min(n-x, y)),其中n为要拆解的数字,而min()表示取两者中较小的数。

使用一个二维阵列表格table[x][y]来表示f(x, y),刚开始时,将每列的索引0与索引1元素值设定为1,因为任何数以0以下的数拆解必只有1种,而任何数以1以下的数拆解也必只有1种:

for(i = 0; i < NUM +1; i++){

table[i][0] = 1; // 任何数以0以下的数拆解必只有1种

table[i][1] = 1; // 任何数以1以下的数拆解必只有1种

}

接下来就开始一个一个进行拆解了,如果数字为NUM,则我们的阵列维度大小必须为NUM x (NUM/2+1),以数字10为例,其维度为10 x 6我们的表格将会如下所示:

1 1 0 0 0 0

1 1 0 0 0 0

1 1 2 0 0 0

1 1 2 3 0 0

1 1 3 4 5 0

1 1 3 5 6 7

1 1 4 7 9 0

1 1 4 8 0 0

1 1 5 0 0 0

1 1 0 0 0 0

实作

#include <stdio.h>
#include <stdlib.h>
#define NUM 10 // 要拆解的数字
#define DEBUG 0
int main(void) { int table[NUM][NUM/2+1] = {0}; // 动态规画表格int count = 0; int result = 0; int i, j, k; printf("数字拆解\n"); printf("3 = 2+1 = 1+1+1 所以3有三种拆法\n"); printf("4 = 3 + 1 = 2 + 2 = 2 + 1 + 1 = 1 + 1 + 1 + 1");printf("共五种\n"); printf("5 = 4 + 1 = 3 + 2 = 3 + 1 + 1");printf(" = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 +1 +1 +1");printf("共七种\n"); printf("依此类推,求 %d 有几种拆法?", NUM); // 初始化for(i = 0; i < NUM; i++){ table[i][0] = 1; // 任何数以0以下的数拆解必只有1种table[i][1] = 1; // 任何数以1以下的数拆解必只有1种
    }// 动态规划for(i = 2; i <= NUM; i++){ for(j = 2; j <= i; j++){ if(i + j > NUM) // 大于 NUM continue; count = 0;for(k = 1 ; k <= j; k++){ count += table[i-k][(i-k >= k) ? k : i-k];} table[i][j] = count; }} // 计算并显示结果for(k = 1 ; k <= NUM; k++) result += table[NUM-k][(NUM-k >= k) ? k : NUM-k];printf("\n\nresult: %d\n", result); if(DEBUG) { printf("\n除错资讯\n"); for(i = 0; i < NUM; i++) { for(j = 0; j < NUM/2+1; j++) printf("%2d", table[i][j]); printf("\n"); } } return 0;
}

全排列在笔试面试中很热门,因为它难度适中,既可以考察递归实现,又能进一步考察非递归的实现,便于区分出考生的水平。所以在百度和迅雷的校园招聘以及程序员和软件设计师的考试中都考到了,因此本文对全排列作下总结帮助大家更好的学习和理解。对本文有任何补充之处,欢迎大家指出。

首先来看看题目是如何要求的(百度迅雷校招笔试题)。

用C++写一个函数, 如 Foo(const char *str), 打印出 str 的全排列,

如 abc 的全排列: abc, acb, bca, dac, cab, cba

一.全排列的递归实现

为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑213和321这二个数是如何得出的。显然这二个都是123中的1与后面两数交换得到的。然后可以将123的第二个数和每三个数交换得到132。同理可以根据213和321来得231和312。因此可以知道——全排列就是从第一个数字起每个数分别与它后面的数字交换。找到这个规律后,递归的代码就很容易写出来了:

//全排列的递归实现
#include <stdio.h>
#include <string.h>
void Swap(char *a, char *b)
{char t = *a;*a = *b;*b = t;
}
//k表示当前选取到第几个数,m表示共有多少数.
void AllRange(char *pszStr, int k, int m)
{if (k == m){static int s_i = 1;printf(" 第%3d个排列\t%s\n", s_i++, pszStr);}else{for (int i = k; i <= m; i++) //第i个数分别与它后面的数字交换就能得到新的排列
        {Swap(pszStr + k, pszStr + i);AllRange(pszStr, k + 1, m);Swap(pszStr + k, pszStr + i);}}
}
void Foo(char *pszStr)
{AllRange(pszStr, 0, strlen(pszStr) - 1);
}
int main()
{printf(" 全排列的递归实现\n");printf(" --by MoreWindows( http://blog.csdn.net/MoreWindows )--\n\n");char szTextStr[] = "123";printf("%s的全排列如下:\n", szTextStr);Foo(szTextStr);return 0;
}

运行结果如下:

注意这样的方法没有考虑到重复数字,如122将会输出:

这种输出绝对不符合要求,因此现在要想办法来去掉重复的数列。

二.去掉重复的全排列的递归实现

由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。

这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。用编程的话描述就是第i个数与第j个数交换时,要求[i,j)中没有与第j个数相等的数。下面给出完整代码:

//去重全排列的递归实现
#include <stdio.h>
#include <string.h>
void Swap(char *a, char *b)
{char t = *a;*a = *b;*b = t;
}
//在pszStr数组中,[nBegin,nEnd)中是否有数字与下标为nEnd的数字相等
bool IsSwap(char *pszStr, int nBegin, int nEnd)
{for (int i = nBegin; i < nEnd; i++)if (pszStr[i] == pszStr[nEnd])return false;return true;
}
//k表示当前选取到第几个数,m表示共有多少数.
void AllRange(char *pszStr, int k, int m)
{if (k == m){static int s_i = 1;printf(" 第%3d个排列\t%s\n", s_i++, pszStr);}else{for (int i = k; i <= m; i++) //第i个数分别与它后面的数字交换就能得到新的排列
        {if (IsSwap(pszStr, k, i)) {Swap(pszStr + k, pszStr + i);AllRange(pszStr, k + 1, m);Swap(pszStr + k, pszStr + i);}}}
}
void Foo(char *pszStr)
{AllRange(pszStr, 0, strlen(pszStr) - 1);
}
int main()
{printf(" 去重全排列的递归实现\n");printf(" --by MoreWindows( http://blog.csdn.net/MoreWindows )--\n\n");char szTextStr[] = "122";printf("%s的全排列如下:\n", szTextStr);Foo(szTextStr);return 0;
}

运行结果如下:

OK,到现在我们已经能熟练写出递归的方法了,并且考虑了字符串中的重复数据可能引发的重复数列问题。那么如何使用非递归的方法来得到全排列了?

三.全排列的非递归实现

要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。

如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。

对于像"4321"这种已经是最“大”的排列,采用STL中的处理方法,将字符串整个颠倒得到最“小”的排列"1234"并返回false。

这样,只要一个循环再加上计算字符串下一个排列的函数就可以轻松的实现非递归的全排列算法。按上面思路并参考STL中的实现源码,不难写成一份质量较高的代码。值得注意的是在循环前要对字符串排序下,可以自己写快速排序的代码(请参阅《白话经典算法之六 快速排序 快速搞定》),也可以直接使用VC库中的快速排序函数(请参阅《使用VC库函数中的快速排序函数》)。下面列出完整代码:

//全排列的非递归实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Swap(char *a, char *b)
{char t = *a;*a = *b;*b = t;
}
//反转区间
void Reverse(char *a, char *b)
{while (a < b)Swap(a++, b--);
}
//下一个排列
bool Next_permutation(char a[])
{char *pEnd = a + strlen(a);if (a == pEnd)return false;char *p, *q, *pFind;pEnd--;p = pEnd;while (p != a){q = p;--p;if (*p < *q) //找降序的相邻2数,前一个数即替换数
        {//从后向前找比替换点大的第一个数pFind = pEnd;while (*pFind <= *p)--pFind;//替换
            Swap(pFind, p);//替换点后的数全部反转
            Reverse(q, pEnd);return true;}}Reverse(p, pEnd);//如果没有下一个排列,全部反转后返回truereturn false;
}
int QsortCmp(const void *pa, const void *pb)
{return *(char*)pa - *(char*)pb;
}
int main()
{printf(" 全排列的非递归实现\n");printf(" --by MoreWindows( http://blog.csdn.net/MoreWindows )--\n\n");char szTextStr[] = "abc";printf("%s的全排列如下:\n", szTextStr);//加上排序qsort(szTextStr, strlen(szTextStr), sizeof(szTextStr[0]), QsortCmp); int i = 1;do{printf("第%3d个排列\t%s\n", i++, szTextStr);}while (Next_permutation(szTextStr));return 0;
}

测试一下,结果如下所示:

将字符串改成"cba"会输出:

至此我们已经运用了递归与非递归的方法解决了全排列问题,总结一下就是:

1.全排列就是从第一个数字起每个数分别与它后面的数字交换。

2.去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。

3.全排列的非递归就是由后向前找替换数和替换点,然后由后向前找第一个比替换数大的数与替换数交换,最后颠倒替换点后的所有数据。

转载于:https://www.cnblogs.com/xkfz007/archive/2012/11/13/2767406.html

算法题-排列组合问题相关推荐

  1. C++经典算法题-排列组合

    27.Algorithm Gossip: 排列组合 说明 将一组数字.字母或符号进行排列,以得到不同的组合顺序,例如1 2 3这三个数的排列组合有: 1 2 3.1 3 2.2 1 3.2 3 1.3 ...

  2. [算法] 求排列组合: 从n个数中任选m个数组成一个新数

    #include <iostream> #include <vector>using namespace std;// 求排列组合算法: C(n, m): 从n个数中任选m个数 ...

  3. 程序员必备算法,排列组合

    还记得排列组合吗? 在高中的时候最常接触的莫过于排列组合了,毕竟高考必考的嘛.我们先来回忆下这两个的公式是啥: 排列组合公式 如果看到这个还有一丢丢的印象,说明大家的基础都还不错.那么问题来了,大家都 ...

  4. DFS算法模板-排列组合-python

    DFS算法模板: def dfs(array or root, cur_layer, path, result):if cur_layer == len(array) or not root:resu ...

  5. 算法笔记 --- 排列组合

    排列组合的计算公式 转载于:https://www.cnblogs.com/zhongzhiqiang/p/5809116.html

  6. 数学和算法之---排列组合

    本文大部分内容摘自网络只是本人稍加整理分享,供大家学习交流. 排列的概念 从n个不同的元素中,任取m(m<=n)个元素按照一定的顺寻排成一列,叫做从n个不同元素中取出m个元素的一个排列.从n个不 ...

  7. 必知C++算法之排列组合基本操作

    概率组合题目分类 1.高中数学为基础的古典概率计算方法 2.斐波那契数列和卡特兰数 6x9的方格,从左上角到右下角,每次只能向下或向右,一共多少种不同走法 一共13步,五步向下,剩下8步向右 组合问题 ...

  8. 算法题 硬币组合问题

    原文:http://www.cnblogs.com/python27/archive/2013/09/05/3303721.html 问题描述 假设我们有8种不同面值的硬币{1,2,5,10,20,5 ...

  9. 算法 64式 17、排列组合算法整理

    1算法思想 排列组合 1.1含义 排列: 含义:从元素列表中取出指定个数的元素进行排序 公式:从n个不同元素中取出m个元素的排列个数=n!/(n-m)! 组合: 含义:从元素列表中取出指定个数的元素, ...

最新文章

  1. 抄代码的时候总是遇到原始数据应该长什么样的问题??
  2. SVN checkout
  3. 依然在那条路上奋斗着
  4. Kubernetes Deployment与Replica Set
  5. Hibernate组件(Component)映射
  6. ASP.NET MVC4 传递Model到View
  7. 5.6 date:显示与设置系统时间
  8. 电脑测速软件_iPerf3 搭建局域网内部测速环境
  9. 央行变相降准祭出又一新手段 引发同业套利之忧
  10. 随想录(关于论文投稿)
  11. Linux下单独编译安装PHP扩展包
  12. Utility Lambda
  13. Pycharm 2018 激活 亲测有效
  14. 关于萤石云与海康威视摄像头对接
  15. MINA框架客户端的使用
  16. geem2登陆器修改服务器列表,Gee引擎怎么更换登陆器皮肤 GeeM2传奇编辑自定义皮肤的方法讲解...
  17. 鼠标控制c语言扫雷程序,【源码项目】C语言/C+开发,打造一个小项目扫雷小游戏!...
  18. html打开网页过场动画_一款谷歌(Google)打造的广告网页设计制作软件
  19. 世界各国简称 英文名称 电话区号JSON数据包
  20. 魔兽服务器维护一般要多少时间,《魔兽世界》魔兽世界维护时间心得

热门文章

  1. 2018 php面试题
  2. strstr strcmp
  3. Active: inactive (dead)
  4. 2021-3-17 Jmeter线程组、运行启动方式
  5. 设计不会崩溃的健康码系统
  6. MFRC522模块测试
  7. java修改密码代码_Java用户修改密码
  8. Servlet中forward和redirect的区别(转)
  9. C3D-network论文笔记
  10. 微信小程序源代码_移动端商城(微信小程序)