技术标签: 算法 C++ # 算法 # 经典问题 # C++ 组合数 数学
从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合;从n个不同元素中取出m(m≤n)个元素的所有组合的个数,叫做从n个不同元素中取出m个元素的组合数。
在线性写法中被写作C(m,n)。
c(m,n)=p(m,n)/n!=m!/((m-n)!*n!)
1.互补性质
组合数性质如右图所示:
即从m个不同元素中取出n个元素的组合数=从m个不同元素中取出(m-n)个元素的组合数
这个性质很容易理解,例如C(9,2)=C(9,7),即从9个元素里选择2个元素的方法与从9个元素里选择7个元素的方法是相等的。
规定:C(m,0)=1
2.组合恒等式
若表示在n个物品中选取m个物品,则如存在下述公式: C(n,m)= C(n,n-m)= C(n-1,m-1)+C(n-1,m)
直接套公式:
求出m!、n!、(m-n)!然后带人公式;
代码如下:
# include<stdio.h>
# include<math.h>
int f(int n){
int i,m=1;
for(i=1;i<=n;i++){
m=m*i;
}
return m;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
printf("%d\n",f(n)/(f(m)*f(n-m)));
return 0;
}
稍微用点小技巧
long long C(int n,int m)
{
if(m<n-m) m=n-m;
long long ans=1;
for(int i=m+1;i<=n;++i) ans*=i;
for(int i=1;i<=n-m;++i) ans/=i;
return ans;
}
就算是long long最多20!左右;再大就会爆了
那就用到我们大数学家杨辉的三角了
: C(n,m)= C(n,n-m)= C(n-1,m-1)+C(n-1,m)
#include<iostream>
using namespace std;
long long c[100][100];
inline void get_it(int n)
{
c[0][0]=1;
for (int i=1;i<=n;i++)
for (int j=0;j<=i;j++)
if (i==j ||j==0) c[i][j]=1;
else c[i][j]=c[i-1][j]+c[i-1][j-1];
}
int main()
{
get_it(60);
for (int i=1;i<=60;i++)
{for (int j=1;j<=i;j++)
cout<<c[i][j]<<" ";
cout<<endl;}
}
理论上只要数组开的出来都可以,最多,emmm,反正比一般方法强。
这个要用快速幂:https://blog.csdn.net/weixin_43272781/article/details/85058595
主要是用在一般方法;求阶乘的时候。
我就不写了。
以上方法最多也就到long long的极限,当然超过long long的我们也存储不下了,但是如果我们只需要一部分高阶组合数,用杨辉三角太浪费了吧。而且一般题目会有求余的要求,那么接下来就是大招了。
证明:https://blog.csdn.net/arrowlll/article/details/52629448
1).扩展欧几里德:b*x+p*y=1 有解,x就是所求
2).费马小定理:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)
1. n!/(m!*(n-m)! =x%p ,先对算出n!、m!、(n-m)!对p取模的余数,就转换为a/b=x%p;因为p为素数,所以等价于bx+py=a;然后用扩展的欧几里得定理算出 bx’+py’=1的解,x=x’*a,就得到了最终的x的值,即C(m,n)%p得值。
2.逆元 其实如果mod是素数 则b的逆元其实就是b^(mod-2)
也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 ;
int inv(int a) {
//return fpow(a, MOD-2, MOD);
return a == 1 ? 1 : (long long)(MOD - MOD / a) * inv(MOD % a) % MOD;
}
LL C(LL n,LL m)
{
if(m < 0)return 0;
if(n < m)return 0;
if(m > n-m) m = n-m;
LL up = 1, down = 1;
for(LL i = 0 ; i < m ; i ++){
up = up * (n-i) % MOD;
down = down * (i+1) % MOD;
}
return up * inv(down) % MOD;
}
3.当n和m比较大,mod是素数且比较小的时候(10^5左右),通过Lucas定理计算
Lucas定理:A、B是非负整数,p是质数。A B写成p进制:A=a[n]a[n-1]…a[0],B=b[n]b[n-1]…b[0]。
则组合数C(A,B)与C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0]) mod p同余
即:Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)
#include<iostream>
//#include<algorithm>
using namespace std;
typedef long long ll;
int quick_power_mod(int a,int b,int m){//pow(a,b)%m
int result = 1;
int base = a;
while(b>0){
if(b & 1==1){
result = (result*base) % m;
}
base = (base*base) %m;
b>>=1;
}
return result;
}
//计算组合数取模
ll comp(ll a, ll b, int p) {//composite num C(a,b)%p
if(a < b) return 0;
if(a == b) return 1;
if(b > a - b) b = a - b;
int ans = 1, ca = 1, cb = 1;
for(ll i = 0; i < b; ++i) {
ca = (ca * (a - i))%p;
cb = (cb * (b - i))%p;
}
ans = (ca*quick_power_mod(cb, p - 2, p)) % p;
return ans;
}
ll lucas(ll n, ll m, ll p) {
ll ans = 1;
while(n&&m&&ans) {
ans = (ans*comp(n%p, m%p, p)) % p;//also can be recusive
n /= p;
m /= p;
}
return ans;
}
int main(){
ll m,n;
while(cin>>n>>m){
cout<<lucas(n,m,10007)<<endl;
}
return 0;
}
C(n % mod, m % mod) % mod; 如果太大还是利用上面的逆元来处理。
半预处理
由于Lucas定理保证了阶乘的数均小于p,所以可以讲所有的阶乘先预处理,优化C(n,m)
mod的要求:p<10^6,且为素数
有效范围:1<=n,m<=10^9
//半预处理
const ll MAXN = 100000;
ll fac[MAXN+100];
void init(int mod)
{
fac[0] = 1;
for (int i=1; i<mod; i++){
fac[i] = fac[i-1] * i % mod;
}
}
//半预处理逆元求C(n,m)%mod
ll C(ll n, ll m)
{
if ( m>n ) return 0;
return fac[n] * (GetInverse(fac[m]*fac[n-m], mod)) % mod;
}
typedef long long LL;
const LL maxn(1000005), mod(1e9 + 7);
LL Jc[maxn];
void calJc() //求maxn以内的数的阶乘
{
Jc[0] = Jc[1] = 1;
for(LL i = 2; i < maxn; i++)
Jc[i] = Jc[i - 1] * i % mod;
}
/*
//拓展欧几里得算法求逆元
void exgcd(LL a, LL b, LL &x, LL &y) //拓展欧几里得算法
{
if(!b) x = 1, y = 0;
else
{
exgcd(b, a % b, y, x);
y -= x * (a / b);
}
}
LL niYuan(LL a, LL b) //求a对b取模的逆元
{
LL x, y;
exgcd(a, b, x, y);
return (x + b) % b;
}
*/
//费马小定理求逆元
LL pow(LL a, LL n, LL p) //快速幂 a^n % p
{
LL ans = 1;
while(n)
{
if(n & 1) ans = ans * a % p;
a = a * a % p;
n >>= 1;
}
return ans;
}
LL niYuan(LL a, LL b) //费马小定理求逆元
{
return pow(a, b - 2, b);
}
LL C(LL a, LL b) //计算C(a, b)
{
return Jc[a] * niYuan(Jc[b], mod) % mod
* niYuan(Jc[a - b], mod) % mod;
}
https://blog.csdn.net/weixin_43272781/article/details/85269419
目录 1. 支付宝支付 - 配置 2. 支付宝支付 - 电脑网站支付一、引用dll二、发起支付/// &lt;summary&gt;/// 支付/// &lt;/summary&gt;/// &lt;param name="aliMsg"&gt;支付宝信息&lt;/param&gt;/// &lt;param name="totalAmount"&gt
原文:https://www.cnblogs.com/hahazexia/p/9446585.html在学习js的时候,或者面试的时候,会经常碰到这一道经典题目:for(var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); });}console.log('a');熟悉这道题目的人立马就可以说出答案:'a'55555结果是先打印字符串'a',然后再打印5个
数据传输指令实现寄存器和内存,寄存器和寄存器之间的单个数据传送。mov(1)MOV 寄存器 , 寄存器/内存单元/段寄存器/立即数(2)MOV 内存单元 , 寄存器/段寄存器/立即数(3)MOV 段寄存器, 寄存器/内存单元非法指令(1)操作数类型不一致,例如mov ax,blax是十六位寄存器,bl是8位寄存器(2)不能修改cs段寄存器内容(3)不能在内存之间直接传...
Classified Problems on Online Judge练习题选自以下在线测评系统* sicily:http://soj.me, 中山大学Sicily在线测评系统* UVA OnlineJudge, 题号前缀为uva* ProgrammingChanlanges Online Judge, 题号前缀为pc题目的分类仅供参考,很多
在BP神经网络中,单个样本有个输入,有个输出,在输入层和输出层之间通常还有若干个隐含层。实际上,1989Robert Hecht-Nielsen证明了对于任何闭区间内的一个连续函数都可以用一个隐含层的BP网络来逼近,这就是万能逼近定理。所以一个三层的BP网络就可以完成任意的维到维的映射。即这三层分别是输入层(I),隐含层(H),输出层(O)。如下图示:公共号“数据统计分析...
目录试题一(30分)试题二(45分)试题三(15分)试题四(20分)实操考试答案评分细则2020年下半年 Web前端开发中级 理论考试考生姓名:——————————— 准考证号:——————————试题一(30分)阅读下列说明、效果图和HTML代码,进行静态网页开发,填写(1)至(10)代码。【说明】这是一个响应式完成,用bootstrap4完成响应式轮播效果。项目采用Bootstrap框架,包含首页index.html、css文件夹、js文件夹、img文件夹,其中
作者:阿甫哥哥原文来自:https://bbs.ichunqiu.com/thread-43341-1-1.html 0×01 前言事情起源于前几天,我一哥们,在pyq喊我们帮他投票,说是奖励巨丰厚。说实话,我是不信天上掉馅饼这种事情的。。。而且那个公众号并不知名,那个代购我也没听说过,所以,我没有投票。。。 ~~~~~~智商分水岭~~~~~~~ 两天后,他...
elementUI官网给的简单实例是<el-popover placement="top-start" title="标题" width="200" trigger="hover" content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"> <el-button slot="reference">hover 激活</el-button> </el-popover>当我们需要动态绑定
文章目录同步模式之保护性暂停1. 定义2. 实现3. 带超时版 GuardedObject4. 多任务版 GuardedObject同步模式之 Balking1. 定义2. 实现同步模式之顺序控制1. 固定运行顺序2. 交替输出异步模式之生产者/消费者1. 定义2. 实现异步模式之工作线程1. 定义2. 饥饿3. 创建多少线程池合适4. 自定义线程池终止模式之两阶段终止模式1. 错误思路2. 两阶段终止模式线程安全单例1. 饿汉单例2. 枚举单例3. 懒汉单例4. DCL 懒汉单例5. 静态内部类懒汉单例享
平时查看PDF的时候明明PDF文档可以正常打开,但却无法转换而且提示要输入密码,这是怎么回事呢?出现这种情况一般是PDF设置了安全性加密,禁止转换或编辑等操作,我们只需要解密就能正常转换啦。我有个方法,简单好用,我们一起看一下如何解决吧!安装okfone PDF解密大师下载地址,打开软件,点击【解除限制】将PDF文件添加进去,选择好输出路径,点击【开始】就可以了很快就可以转换好了,点击【打开文件夹】就可以找到解除限制的PDF文件了...
C++解决多处最优服务次序
学习交流欢迎加群:789723098,博主会将一些demo整理共享很多学习WebGL的小伙伴,刚开始一直都是学怎么画立方体,等到立方体画的炉火纯青的时候,却被另一个东西难住了,那就是球。what,还会被一个球难住?下面就给大家介绍一种画球的算法:我们的地球现在被划分为很多经线和纬线,如下图所示:由下面的图,我们来推导一下计算球面上点的坐标:假设图中圆的半径R=1...