数学
数学是算法的根源、学好数学才能在算法这条路上走的更远、
最基本的是求 GCD ( 最大公约数)
可以用递归和迭代的方式实现、一般为了代码简洁都会用递归实现、
代码如下、复杂度O(logmax(a,b))
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
接下来是素数相关的东西、素数在数学中的地位相当重要、有关素数的算法非常多、
首先我们判断一个数是不是素数、
素性测试、复杂度O(sqrt(n))
bool is_prime(int n) {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return n != 1; // 1是例外
}
然后我们枚举一个数的约数有哪些、复杂度O(sqrt(n))
vector<int> divisor(int n) {
vector<int> res;
for (int i = 1; i * i <= n; i++) {
if (n % i ==0) {
res.push_back(i);
if (i != n / i) res.push_back(n / i);
}
}
}
接下来是素数打表、埃氏筛选法、复杂度O(nloglogn)
int prime[MAX_N]; // 第i个素数
bool is_prime[MAX_N + 1]; // is_prime[i]为true表示i是素数
// 返回n以内素数的个数
int sieve(int n) {
int p = 0;
for (int i = 0; i <= n; i++) is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
prime[p++] = i;
for (int j = 2 * i; j <= n; j += i) is_prime[j] = false;
}
}
return p;
}
线性筛选法
// 素数筛选、存在小于等于MAXN的素数
// prime[0] 存的是素数的个数
const int MAXN = 10000;
int prime[MAXN + 1];
void GetPrime() {
memset(prime, 0, sizeof (prime));
for (int i = 2; i <= MAXN; i++) {
if (!prime[i]) prime[++prime[0]] = i;
for (int j = 1; j <= prime[0] && prime[j] <= MAXN / i; j++) {
prime[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
}
}
}
欧拉函数
欧拉函数的值等于不超过m并且和m互素的数的个数
如果x和m互素 x^(elur(m)) = 1(mod m)
求欧拉函数值 复杂度O(sqrt(n))
int euler_phi(int n) {
int res = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
for (; n % i == 0; n /= i);
}
}
if (n != 1) res = res / n * (n - 1);
return res;
}
O(MAX_N)时间筛出欧拉函数值的表
int euler[MAX_N];
void euler_phi2() {
for (int i = 0; i < MAX_N; i++) euler[i] = i;
for (int i = 2; i < MAX_N; i++) {
if (euler[i] == i) {
for (int j = i; j < MAX_N; j += i) euler[j] = euler[j] / i * (i - 1);
}
}
}
二分快速幂运算
复杂度 O(logn)
typedef long long ll;
ll mod_pow(ll x, ll y, ll mod) {
ll res = 1;
while (n > 0) {
if (n & 1) res = res * x % mod; //如果二进制最低位为1、则乘上x^(2^i)
x = x * x % mod; // 将x平方
n >>= 1;
}
return res;
}
矩阵快速幂运算
const int MOD = 1e9 + 7;
struct Matrix {
long long mat[2][2];
};
Matrix mul(Matrix a, Matrix b) {
Matrix ret;
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
ret.mat[i][j] = 0;
for(int k = 0; k < 2; k++) {
ret.mat[i][j] += a.mat[i][k] * b.mat[k][j];
ret.mat[i][j] %= (MOD - 1);
}
}
}
return ret;
}
Matrix pow_M(Matrix a,int n) {
Matrix ret;
memset(ret.mat, 0, sizeof(ret.mat));
ret.mat[0][0] = ret.mat[1][1] = 1;
Matrix temp = a;
while (n) {
if (n&1) ret = mul(ret, temp);
temp = mul(temp, temp);
n >>= 1;
}
return ret;
}
随机素性测试大数分解
给定一个64位整数,问是否为质数,如果不是,则输出其最小因子。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
//****************************************************************
// Miller_Rabin 算法进行素数测试
//速度快,而且可以判断 <2^63的数
//****************************************************************
const int S=8;//随机算法判定次数,S越大,判错概率越小 S一般取8-20
//计算 (a*b)%c. a,b都是long long的数,直接相乘可能溢出的
// a,b,c <2^63
long long mult_mod(long long a,long long b,long long c)
{
a%=c;
b%=c;
long long ret=0;
while(b)
{
if(b&1){ret+=a;ret%=c;}
a<<=1;
if(a>=c)a%=c;
b>>=1;
}
return ret;
}
//计算 x^n %c
long long pow_mod(long long x,long long n,long long mod)//x^n%c
{
if(n==1)return x%mod;
x%=mod;
long long tmp=x;
long long ret=1;
while(n)
{
if(n&1) ret=mult_mod(ret,tmp,mod);
tmp=mult_mod(tmp,tmp,mod);
n>>=1;
}
return ret;
}
//以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数
//一定是合数返回true,不一定返回false
bool check(long long a,long long n,long long x,long long t)
{
long long ret=pow_mod(a,x,n);
long long last=ret;
for(int i=1;i<=t;i++)
{
ret=mult_mod(ret,ret,n);
if(ret==1&&last!=1&&last!=n-1) return true;//合数
last=ret;
}
if(ret!=1) return true;
return false;
}
// Miller_Rabin()算法素数判定
//是素数返回true.(可能是伪素数,但概率极小)
//合数返回false;
bool Miller_Rabin(long long n)
{
if(n<2)return false;
if(n==2)return true;
if((n&1)==0) return false;//偶数
long long x=n-1;
long long t=0;
while((x&1)==0){x>>=1;t++;}
for(int i=0;i<S;i++)
{
long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件
if(check(a,n,x,t))
return false;//合数
}
return true;
}
//************************************************
//pollard_rho 算法进行质因数分解
//************************************************
long long factor[100];//质因数分解结果(刚返回时是无序的)
int tol;//质因数的个数。数组小标从0开始
long long gcd(long long a,long long b)
{
if(a==0)return 1;//???????
if(a<0) return gcd(-a,b);
while(b)
{
long long t=a%b;
a=b;
b=t;
}
return a;
}
long long Pollard_rho(long long x,long long c)
{
long long i=1,k=2;
long long x0=rand()%x;
long long y=x0;
while(1)
{
i++;
x0=(mult_mod(x0,x0,x)+c)%x;
long long d=gcd(y-x0,x);
if(d!=1&&d!=x) return d;
if(y==x0) return x;
if(i==k){y=x0;k+=k;}
}
}
//对n进行素因子分解
void findfac(long long n)
{
if(Miller_Rabin(n))//素数
{
factor[tol++]=n;
return;
}
long long p=n;
while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
findfac(p);
findfac(n/p);
}
int main()
{
// srand(time(NULL));//需要time.h头文件 //POJ上G++要去掉这句话
int T;
long long n;
scanf("%d",&T);
while(T--)
{
scanf("%I64d",&n);
if(Miller_Rabin(n))
{
printf("Prime\n");
continue;
}
tol=0;
findfac(n);
long long ans=factor[0];
for(int i=1;i<tol;i++)
if(factor[i]<ans)
ans=factor[i];
printf("%I64d\n",ans);
}
return 0;
}
数学中另一个比较基础的东西就是模运算、其中同余模定理是核心、
约瑟夫环问题
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。
f[1]=0; f[i]=(f[i-1]+m)%i; (i>1) 下标从0开始
f[1]=1; f[i]=(f[i-1]+m)%i (i>1); if(f[i]==0) f[i]=i; 下标从1开始
线性同余方程
数学表达式为ai * x = bi(mid mi)(1 <= i <= n)
解的全集可以写成x = b(mod m) 问题就转化为了求b和m
// 返回一个(b, m)的数对
pair<int, int> linear_congruence(const vector<int>& A, const vector<int>& B, const vector<int>& M) {
//由于最开始没有任何限制、所以先把解设为表示所有整数的x = 0(mod 1)
int x = 0, m = 1;
for (int i = 0; i < A.size(); i++) {
int a = A[i] * m, b = B[i] - A[i] * x, d = gcd(M[i], a);
if (b % d != 0) return make_pair(0, -1); // 无解
int t = b / d * mod_inverse(a / d,M[i] / d) % (M[i] / d);
x = x + m * t;
m *= M[i] / d;
}
return make_pair(x % m, m);
}
中国剩余定理
假设同余方程组里所有的ai都等于1,并且所有的mi都互素,答案就一定是x = b(mod m1*m2*...*mi)
对于一个合数n,我们假设n=ab(其中a和b互素)
那么(x mod n) == (x mod a, x mod b)
换句话说,以合数n为模数来考虑与以a和b为模数来考虑是等价的
其中n不能被任何一个完全平方数整除
扩展GCD
ax + by = gcd(a, b) 恒成立
ax + by = 1 gcd(a, b)等于1时有解
int extgcd(int a, int b, int &x, int &y) {
int d = a;
if(b != 0) {
d = extgcd(b, a % b, y, x);
y -= (a / b) * x;
} else {
x = 1; y = 0;
}
return d;
}
求逆元
ax = 1(mod m)等价于存在整数k使得ax = 1 + mk
变形得到ax - mk = 1 如果gcd(a, m)!=1那么逆元不存在
int mod_inverse(int a, int m) {
int x, y;
extgcd(a, m, x, y);
return (m + x % m) % m;
}
费马小定理
如果p是素数 x^p = x(mod p)
如果x无法被p整除、则x^(p - 1)= 1(mod p)
a^(-1) = a^(p - 2)(mod p) 可以通过快速幂求出逆元
组合数取模
如果预处理出1<=n<p范围中n!mod p 的表就可以在O(logp n)时间内算出答案
否则复杂度是O(plogp n)
// 求nCk mod p O(logp n)
int mod_comb(int n, int k, int p) {
if (n < 0 || k < 0 || n < k) return 0;
int e1, e2, e3;
int a1 = mod_fact(n, p, e1);
int a2 = mod_fact(k, p, e2);
int a3 = mod_fact(n - k, p, e3);
return a1 * mod_inverse(a2 * a3 % p, p) % p;
}
Lucas定理
A、B是非负整数,p是质数。AB写成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]) modp同余
即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)
对于大组合数取模,n,m不大于10^5的话,用逆元的方法,可以解决。对于n,m大于10^5的话,那么要求p<10^5,这样就是Lucas定理了,将n,m转化到10^5以内解。
推广:奇偶性质(联系2进制分析) 0C1=1 0C0=1C0=1C1=1 注:14年上海网络赛B题也是一个Lucas定理的应用
long long Lucas(long long a, long long k, long long p) {
//求C(n,m)%p p最大为10^5。a,b可以很大!
long long re = 1;
while (a && k) {
long long aa = a % p;
long long bb = k % p;
if (aa < bb) return 0; //C(aa,bb) 表示 在aa里面取bb个,取法为0
//由于p是素数,所以 a / b % p ,b 对于 mod a 肯定存在逆元
re = re * fact[aa] * mod_pow(fact[bb] * fact[aa-bb] % p, p - 2, p) % p;
//这儿的求逆不可先处理
a /= p;
k /= p;
}
return re;
}
N!%p
假设p是一个素数
int fact[MAX_P]; // 预处理的n! mod p 的表 O(p)
// 分解n!=a*p^e 返回a mod p O(logp n)
int mod_fact(int n, int p, int& e) {
e = 0;
if (n == 0) return 1;
// 计算p的倍数的部分
int res =mod_fact(n / p, p, e);
e += n / p;
// 由于(p-1)!=-1 因此(p-1)!^(n/p)只需要知道n/p的奇偶性就可以计算了
if (n / p % 2 != 0) return res * (p - fact[n % p]) % p;
return res * fact[n % p] % p;
}
组合数学也是数学中一个经典分支、个人感觉组合比数论更加有趣、
母函数
母函数需要掌握普通母函数和指数型母函数
普通型母函数主要是来求组合的方案数,而指数型母函数是求多重排列数。
学习资料:click here
置换及其运算
置换比较容易理解、普通的计算也只是找循环节、比较有意思的是置换的快速幂和开方、
学习资料:click here
Burnside引理与Polya定理
关于burnside引理其本质就是找出在每一种置换下不变元素的个数
然而使用搜索实现的burnside的统计复杂度过高、于是通过置换群推理得到polya定理、具体证明过程请看论文: 符文杰《Pólya原理及其应用》
下面给出polya定理的公式
注意:对于圆环的模型来说、求循环节可以转化为求gcd、对于求gcd又可以用欧拉函数来优化、
环有len个节点,每个节点必须染色
color == 颜色数(无限)
len == 节点数
ans += color^(gcd(len, i)) (1<=i<=len)
if(len & 1) ans += len * color^(len / 2);
else ans += len / 2 * (color^(len / 2) + color^(len / 2 + 1))
代码如下:
LL polya() {
LL sum = 0;
for (int i = 1; i <= s; i++)
if (s % i == 0)
sum += Pow(c, i) * eular(s / i);
if (s & 1)
sum += s * Pow(c, (s >> 1) + 1);
else
sum += s / 2 * (Pow(c, (s >> 1) + 1) + Pow(c, s >> 1));
return sum / 2 / s;
}
学习资料:click here
鸽巢原理和容斥原理
这两个定理看概念好简单、却杀人于无形- -、智商常年被秀、、、、
容斥定理一般使用二进制压缩来实现:给定一个数组a,求1到n的整数中至少能整除a中一个元素的数有几个
typedef long long ll;
int a[MAX_M];
int n, m;
void solve() {
ll res = 0;
for (int i = 1; i < (1 << m); i++) {
int num = 0;
for (int j = i; j != 0; j >>= 1) num += j & i; // i的二进制表示中1的数量
ll lcm = 1;
for (int j = 0; j < m; j++) {
if (i >> j & 1) {
lcm = lcm / gcd(lcm, a[j]) * a[j];
// 如果lcm大于n,则n/lcm=0 因此在溢出之前break
if (lcm > n) break;
}
}
if (num % 2 == 0) res -= n / lcm;
else res += n / lcm;
}
printf("%d\n", res);
}
鸽巢定理更多用于证明、比如求最近点对中枚举周围最多6个点便是使用鸽巢定理证明、
学习资料:click here
然而容斥定理需要枚举所有集合之间的组合情况、复杂度过高、可用莫比乌斯函数反演来优化、
f(n)=g(d) (d|n) <==> g(n)=u(n/d)*f(d) (d|n)
f(d)是周期为d的约数的字符串的个数、g(d)是周期恰好为d的字符串的个数
由于f(d)已经可以高效的求出了、因此可以由右边的式子推出g(n)
若n可以被除1以外的完全平方数整除 u(n)=0
否则设n的质因数的个数为k u(n)=(-1)^k
由于整数分解可以在O(sqrt(n))时间内完成、所以u(n)也是
如果使用埃氏筛法、可以在O(n)时间内求出1-n所有的u值
求所有的由a-z组成的(不一定要使用所有字母)长度为n的字符串中,没有周期性的字符串的个数。
// 把n的约数的莫比乌斯函数值用map的形式返回 O(sqrt(n))
map<int, int> moebius(int n) {
map<int, int> res;
vector<int> primes;
// 枚举n的质因数
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
primes.push_back(i);
while (n % i == 0) n /= i;
}
}
if(n != 1)primes.push_back(n);
int m = primes.size();
for (int i = 0; i < (1 << m); i++) { // 虽然要执行2^m次、但是这不超过n的约数个数
int mu = 1, d = 1;
for (int j = 0; j < m; j++) {
if (i >> j & 1) {
mu *= -1;
d *= primes[j];
}
}
res[d] = mu;
}
return res;
}
const int MOD = 10009;
//输入
int n;
void solve() {
int res = 0;
map<int, int> mu = moebius(n);
for (map<int, int>::iterator it = mu.begin(); it != mu.end(); ++it) {
res += it->second * mod_pow(26, n / it->first, MOD);
res = (res % MOD + MOD) % MOD;
}
printf("%d\n", res);
}
快速傅里叶变换(FFT)
FFT主要用于多项式乘法、可学习郭晓旭论文《Fourier transform》
高精度乘法模板
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
const double PI = acos(-1.0);
//复数结构体
struct complex
{
double r,i;
complex(double _r = 0.0,double _i = 0.0)
{
r = _r; i = _i;
}
complex operator +(const complex &b)
{
return complex(r+b.r,i+b.i);
}
complex operator -(const complex &b)
{
return complex(r-b.r,i-b.i);
}
complex operator *(const complex &b)
{
return complex(r*b.r-i*b.i,r*b.i+i*b.r);
}
};
/*
* 进行FFT和IFFT前的反转变换。
* 位置i和 (i二进制反转后位置)互换
* len必须去2的幂
*/
void change(complex y[],int len)
{
int i,j,k;
for(i = 1, j = len/2;i < len-1; i++)
{
if(i < j)swap(y[i],y[j]);
//交换互为小标反转的元素,i<j保证交换一次
//i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
k = len/2;
while( j >= k)
{
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
/*
* 做FFT
* len必须为2^k形式,
* on==1时是DFT,on==-1时是IDFT
*/
void fft(complex y[],int len,int on)
{
change(y,len);
for(int h = 2; h <= len; h <<= 1)
{
complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
for(int j = 0;j < len;j+=h)
{
complex w(1,0);
for(int k = j;k < j+h/2;k++)
{
complex u = y[k];
complex t = w*y[k+h/2];
y[k] = u+t;
y[k+h/2] = u-t;
w = w*wn;
}
}
}
if(on == -1)
for(int i = 0;i < len;i++)
y[i].r /= len;
}
const int MAXN = 200010;
complex x1[MAXN],x2[MAXN];
char str1[MAXN/2],str2[MAXN/2];
int sum[MAXN];
int main()
{
while(scanf("%s%s",str1,str2)==2)
{
int len1 = strlen(str1);
int len2 = strlen(str2);
int len = 1;
while(len < len1*2 || len < len2*2)len<<=1;
for(int i = 0;i < len1;i++)
x1[i] = complex(str1[len1-1-i]-'0',0);
for(int i = len1;i < len;i++)
x1[i] = complex(0,0);
for(int i = 0;i < len2;i++)
x2[i] = complex(str2[len2-1-i]-'0',0);
for(int i = len2;i < len;i++)
x2[i] = complex(0,0);
//求DFT
fft(x1,len,1);
fft(x2,len,1);
for(int i = 0;i < len;i++)
x1[i] = x1[i]*x2[i];
fft(x1,len,-1);
for(int i = 0;i < len;i++)
sum[i] = (int)(x1[i].r+0.5);
for(int i = 0;i < len;i++)
{
sum[i+1]+=sum[i]/10;
sum[i]%=10;
}
len = len1+len2-1;
while(sum[len] <= 0 && len > 0)len--;
for(int i = len;i >= 0;i--)
printf("%c",sum[i]+'0');
printf("\n");
}
return 0;
}
利用FFT可以快速求取循环卷积
长度为n的数组和长度为m的数组卷积,结果是长度为n+m-1的数组。
使用FFT一定要注意控制好长度,长度要为2^k.而且大于等于len1+len2-1.这样可以保证不重叠。
题意:给出n条线段长度,问任取3根,组成三角形的概率 n<=10^5
const int MAXN = 400040;
complex x1[MAXN];
int a[MAXN/4];
long long num[MAXN];//100000*100000会超int
long long sum[MAXN];
int main()
{
int T;
int n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(num,0,sizeof(num));
for(int i = 0;i < n;i++)
{
scanf("%d",&a[i]);
num[a[i]]++;
}
sort(a,a+n);
int len1 = a[n-1]+1;
int len = 1;
while( len < 2*len1 )len <<= 1;
for(int i = 0;i < len1;i++)
x1[i] = complex(num[i],0);
for(int i = len1;i < len;i++)
x1[i] = complex(0,0);
fft(x1,len,1);
for(int i = 0;i < len;i++)
x1[i] = x1[i]*x1[i];
fft(x1,len,-1);
for(int i = 0;i < len;i++)
num[i] = (long long)(x1[i].r+0.5);
len = 2*a[n-1];
//减掉取两个相同的组合
for(int i = 0;i < n;i++)
num[a[i]+a[i]]--;
//选择的无序,除以2
for(int i = 1;i <= len;i++)
{
num[i]/=2;
}
sum[0] = 0;
for(int i = 1;i <= len;i++)
sum[i] = sum[i-1]+num[i];
long long cnt = 0;
for(int i = 0;i < n;i++)
{
cnt += sum[len]-sum[a[i]];
//减掉一个取大,一个取小的
cnt -= (long long)(n-1-i)*i;
//减掉一个取本身,另外一个取其它
cnt -= (n-1);
//减掉大于它的取两个的组合
cnt -= (long long)(n-1-i)*(n-i-2)/2;
}
//总数
long long tot = (long long)n*(n-1)*(n-2)/6;
printf("%.7lf\n",(double)cnt/tot);
}
return 0;
}
其他
高斯消元
#define eps 1e-9
const int MAXN = 220;
double a[MAXN][MAXN], x[MAXN];//方程的左边的矩阵和等式右边的值,求解之后x存的就是结果
int equ, var;//方程数和未知数个数
/*
*返回0表示无解,1表示有解
*/
int Gauss() {
int i, j, k, col, max_r;
for (k=0, col=0; k < equ && col < var; k++, col++) {
max_r = k;
for (i = k + 1; i < equ; i++)
if (fabs(a[i][col]) > fabs(a[max_r][col]))
max_r=i;
if (fabs(a[max_r][col]) < eps) return 0;
if (k != max_r) {
for (j = col; j < var; j++)
swap(a[k][j], a[max_r][j]);
swap(x[k], x[max_r]);
}
x[k] /= a[k][col];
for (j = col+1; j < var; j++) a[k][j] /= a[k][col];
a[k][col] = 1;
for (i = 0; i < equ; i++)
if(i != k) {
x[i] -= x[k] * a[i][k];
for (j = col + 1; j < var; j++) a[i][j] -= a[k][j]*a[i][col];
a[i][col] = 0;
}
}
return 1;
}
错排
f[1]=0 f[2]=1 f[n]=(n-1)*(f[n-1]+f[n-2])
f(n) = n![1-1/1!+1/2!-1/3!+……+(-1)^n*1/n!]
卡特兰数
卡特兰数是组合数学中一个常出现在各种计数问题中出现的数列
另类递归式: h(n)=((4*n-2)/(n+1))*h(n-1)
前几项1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440
所有的奇卡塔兰数Cn都满足n = 2^k-1、所有其他的卡塔兰数都是偶数
应用:
Cn表示长度2n的dyck word的个数。Dyck word是一个有n个X和n个Y组成的字串,且所有的部分字串皆满足X的个数大于等于Y的个数。XXXYYY XYXXYY XYXYXY XXYYXY XXYXYY
将上例的X换成左括号,Y换成右括号,Cn表示所有包含n组括号的合法运算式的个数:((())) ()(()) ()()() (())() (()())
Cn表示有n+1个叶子的二叉树的个数。
Cn表示所有不同构的含n个分枝结点的满二叉树的个数。
Cn表示所有在n*n格点中不越过对角线的单调路径的个数。
Cn表示通过连结顶点而将n + 2边的凸多边形分成三角形的方法个数。
几类放苹果问题
苹果m 盘子n 允许有空盘 放法数
不同 不同 允许 n^m
不同 不同 不允许 n!*S(m,n)
不同 相同 允许 S(m,1)+S(m,2)+...+S(m,n),m>=n
S(m,1)+S(m,2)+...+S(m,m),m<=n
不同 相同 不允许 S(m,n)
相同 不同 允许 C(m+n+1,m)
相同 不同 不允许 C(m-1,n-1)
相同 相同 允许 F(n,m)
相同 相同 不允许 F(n,m-n)
F(n,m)是简单放苹果问题。S(m,n)是第二类Stirling数
递推公式:
int F(int m,int n) {
if (m == 0 || n == 1)
return 1;
if (m < n)
return (F(m, m));
else
return (F(m - n, n) + F(m, n - 1));
}
int S(int m, int n) {
if (m == n || n == 1) return 1;
return m * S(n - 1, m) + S(m - 1, m - 1);
}
其实博弈论也应算在数学里面、但是我还是打算单独将博弈列出来、
最后、数学就总结到这里了、
----- 黑白是一种态度




浙公网安备 33010602011771号