求组合数

一:由公式直接求组合数,C(n,m)= n!/ (m! * (n - m)!),预处理打阶乘表

n!限制了n的范围为n <= 20

#define LL long long
LL f[21];
void init() {
	f[0] = 1;
	for (int i = 1; i <= 20; i++) {
		f[i] = f[i - 1] * i;
	}
}
LL C(int n, int m) {
	if (n < m) return 0;
	return f[n] / f[m] / f[n - m];
}

二:化简一下 C(n,m) = n!/ (m! * (n - m)!),假设m = max(m,n - m)

C(n,m)= ((m + 1)*(m + 2)* ......*(n))/ (1 * 2 * ...... *(n - m))

当n = 30,m = 15,分子就超出了long long。

using namespace std;
#define LL long long
LL C(int n, int m) {
	LL re = 1;
	if (n < m) return 0;
	m = max(m, n - m);
	for (int i = m + 1; i <= n; i++) {
		re *= i;
	}
	for (int i = 1; i <= n - m; i++) {
		re /= i;
	}
	return re;
}
三:递推,利用 C(n,m) = C(n - 1,m - 1) + C(n - 1,m),预处理打组合数表,若无取模操作,n、m为68、30时即超出long long范围若用int数组,n、m为35、16时即超出int范围。题目常伴有取模操作(MOD = 1e9 + 7),由于是加法,运算过程是不会溢出的。题目的内存限制一般为32768K(2^25)个字节,最多可以开大小为2^23(八百多万)的int数组,对于二维数组,则n可以取近3000,二维long long数组,n可以取2000多一些,所以用这个方法,应在这个范围内求组合数。(本地机器,数组大小限制为0X7fffffff个字节,试验了一下,对于此算法,long long数组 n可取10000多,若用int数组,n可取20000多,再大会超出内存大小分配限制,内存大小分配限制因机器而异


无模,int数组

int c[35][35];
void C() {
	for (int i = 1; i < 35; i++) {
		c[i][0] = 1;
	}
	for (int i = 1; i < 35; i++) {
		for (int j = 1; j <= i; j++) {
			c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
		}
	}
}

无模,long long数组

#define LL long long
LL c[68][68];
void C() {
	for (int i = 1; i < 68; i++) {
		c[i][0] = 1;
	}
	for (int i = 1; i < 68; i++) {
		for (int j = 1; j <= i; j++) {
			c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
		}
	}
}


有模,int数组

#define MOD 1000000007
int c[2005][2005];
void C() {
	for (int i = 1; i < 2005; i++) {
		c[i][0] = 1;
	}
	for (int i = 1; i < 2005; i++) {
		for (int j = 1; j <= i; j++) {
			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
		}
	}
}

有模,long long数组

#define LL long long
#define MOD 1000000007
LL c[2005][2005];
void C() {
	for (int i = 1; i < 2005; i++) {
		c[i][0] = 1;
	}
	for (int i = 1; i < 2005; i++) {
		for (int j = 1; j <= i; j++) {
			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
		}
	}
}


四:算法三复杂度O(n ^ 2),若只用到最后一层c[n][m],未免浪费时间空间

可利用C(n,m)= C(n,m - 1)* (n - m + 1)/ m在O(n)内完成,注意,应该先乘后除,因为C(n,m - 1)/ m 或者 (n - m + 1)/ m不一定为整数。不取模的话,采用long long 数组或者int数组,在题目内存限制的前提下,n也不能取太大,否则肯定会溢出,就算取模,由于运算为乘法,也保证不了不溢出,而且溢出也不容易被察觉,因为对于乘法,即使结果在int或者long long 范围内,也有可能溢出,这点很危险,所以这个方法不推荐使用,如果使用,是小范围或者中等范围数据才可以,而且要先根据题目数据范围判断溢出的可能性再使用该算法。

#define LL long long
LL c[n];
void C() {
	c[0] = 1;
	for (int i = 1; i < n; i++) {
		c[i] = c[i - 1] * (n - i + 1) / i;
	}
}

五: 采用分解质因子的方式

前几个算法,需要打表的,会受限于内存,运算过程涉及到乘法、加法等的,也要防止溢出,这些因素决定了前几个算法不适用于较大的数据规模。此处采用分解质因子的算法适用于long long级别的大数据,没有用到数组打表预处理,在线计算,从而少了内存限制,没用到四则运算,也不用担心溢出,所以可以计算足够大的数,计算结果一开始依然用质因子表示,因为最后数值结果会超过long long的范围,所以结果一般会取模。

具体实现暂时略

六:乘法逆元

暂时略








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值