acm 组合数学及其应用--母函数
  tU95LmXaBSF2 2023年11月02日 28 0


追逐青春的梦想,怀着自信的心,永不放弃

1、假设有x1个字母A,x2个字母B,……,x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,……,字母Z的价值为26。那么,对于给定的字母,可以找到多少价值不大于50的单词呢?单词的价值就是组成一个单词的所有字幕的价值之和。比如,单词ACM的价值是1+3+14=18(组成的单词与排列顺序无关,比如ACM和MCA认为是同一单词)。

输入:输入首先是一个整数N,代表测试实例的个数。然后包括N行数据,每行包括26个不大于20的整数x1,x2,……,x26
输出:对于每个测试实例,请输出能找到的总价值不大于50的单词数,每个势力的输出占一行。

输入样例:

2
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 2 6 2 10 2 2 5 6 1 0 2 7 0 2 2 7 5 10 6 10 2 10 6 1 9

样例输出:

7
379297

分析:转化为求x的指数小于等于50的系数和

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<map>
#include<stack>
#include<queue>
#include<set>
#include<sstream>
#include<ctime>
#include<algorithm>
#include<bits/stdc++.h>
#include<sstream>
#include<list>
#include<cmath>
#include<deque>
#include<cstdlib>
using namespace std;
const int maxn = 10086;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
typedef long long LL;
void anhangduru(){
string line;
while(getline(cin,line)){
int sum=0;
int x;
stringstream ss(line);
while(ss>>x){sum+=x;}//按空格读入
}
}//按行读入
//加上符号重载
int main()
{
// ios::sync_with_stdio(0);//输入输出挂
int n;
scanf("%d",&n);
int a[60],b[60];
while(n--){
int num;
for(int i=0;i<=60;i++){
a[i]=0;
b[i]=0;
}
a[0]=1;
for(int i=1;i<=26;i++){
scanf("%d",&num);
if(num==0)continue;
for(int j=0;j<=50;j++)
for(int k=0;k<=num&&k*i+j<=50;k++)
b[k*i+j]+=a[j];


for(int j=0;j<=50;j++){
a[j]=b[j];
b[j]=0;
}
}
int tot = 0;
for(int i=1;i<=50;i++){
tot += a[i];
}
printf("%d\n",tot);
}
return 0;
}

这段代码的核心是:

a[0]=1;
for(int i=1;i<=26;i++){
scanf("%d",&num);
if(num==0)continue;
for(int j=0;j<=50;j++)
for(int k=0;k<=num&&k*i+j<=50;k++)
b[k*i+j]+=a[j];
for(int j=0;j<=50;j++){
a[j]=b[j];
b[j]=0;
}
}

2、给一个正整数N,我们定义这样一个等式:

acm 组合数学及其应用--母函数_i++

因此,当N等于4时结果是5。

输入:输入包含几组测试样例。每一组测试样例包含一个正整数N(1<=N<=120)。
输出:对于每个测试实例,请输出能找到的不同等式的个数,每个实例的输出占一行。
样例输入:

4
10
20

样例输出:

5
42
627

分析:求正整数N的拆分数。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<map>
#include<stack>
#include<queue>
#include<set>
#include<sstream>
#include<ctime>
#include<algorithm>
#include<bits/stdc++.h>
#include<sstream>
#include<list>
#include<cmath>
#include<deque>
#include<cstdlib>
using namespace std;
const int maxn = 10086;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
typedef long long LL;
void anhangduru(){
string line;
while(getline(cin,line)){
int sum=0;
int x;
stringstream ss(line);
while(ss>>x){sum+=x;}//按空格读入
}
}//按行读入
//加上符号重载
int main()
{
// ios::sync_with_stdio(0);//输入输出挂
int n;
int a[121],b[121];
while(~scanf("%d",&n)){
for(int i=0;i<=n;i++){
a[i]=1;
b[i]=0;
}
for(int i=2;i<=n;i++){
for(int j=0;j<=n;j++){
for(int k=0;k+j<=n;k+=i){
b[k+j]+=a[j];
}
}
for(int j=0;j<=n;j++){
a[j]=b[j];
b[j]=0;
}
}
printf("%d\n",a[n]);
}
return 0;
}

这段代码的关键点:

for(int i=2;i<=n;i++){
for(int j=0;j<=n;j++){
for(int k=0;k+j<=n;k+=i){
b[k+j]+=a[j];
}
}
for(int j=0;j<=n;j++){
a[j]=b[j];
b[j]=0;
}
}

3、Ferrers图像

定理1:

整数n拆分成k个数的和的拆分数,与数n拆分成最大数为k的拆分数相等

定理2:

整数n拆分成最多不超过m个数的和的拆分数,与n拆分成最大不超过m的拆分数相等

定理3:

整数n拆分成互不相同的若干奇数的和的拆分数,与n拆分成自共轭的Ferrers图像的拆分数相等

定理4:

正整数n拆分成不超过k个数的和的拆分数,等于将n+k拆分成敲好k个数的拆分数

4、指数型母函数

有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有”AB”,”BA”两种。

输入:每组输入数据有两行,第一行是二个数n,m(1<=m,n<=10),表示物品数,第二行有n个数,分别表示这n件物品的数量。

输出:对应每组数据输出排列数。(任何运算不会超出2^31的范围)。
样例输入:

2 2 1 1

样例输出:

2

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
typedef long long LL;
using namespace std;
const int N = 11;
double c1[N],c2[N]; //注意类型
LL fac[N];

void cal(){
fac[0]=1; //0!会被用到
for(int i=1;i<N;i++)
fac[i]=i*fac[i-1];
}

int main(){
cal();
int n,r;
while(~scanf("%d%d",&n,&r)){
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));

c1[0]=1;
int num;
for(int i=1;i<=n;i++){
scanf("%d",&num);
if(num==0) continue;
for(int j=0;j<=r;j++){
for(int k=0;k<=num&&k+j<=r;k++){
c2[k+j]+=c1[j]/fac[k];
}
}
for(int j=0;j<=r;j++){
c1[j]=c2[j];
c2[j]=0;
}
}

printf("%.0lf\n",c1[r]*fac[r]);
}

return 0;
}

关键代码:

c1[0]=1;
int num;
for(int i=1;i<=n;i++){
scanf("%d",&num);
if(num==0) continue;
for(int j=0;j<=r;j++){
for(int k=0;k<=num&&k+j<=r;k++){
c2[k+j]+=c1[j]/fac[k];
}
}
for(int j=0;j<=r;j++){
c1[j]=c2[j];
c2[j]=0;
}
}

与第一个题目是类似的。注意题目描述的差别

5、求n位十进制数中出现偶数个5的数的个数。

这道题可以使用数位dp来解决,但是也可以使用母函数的办法。
令xn表示n位十进制数中出现偶数个5的数的个数,令yn表示n为十进制数中出现奇数个5的数的个数,则有如下分析:
当n>1时:xn = 9xn-1 + yn-1且yn = 9yn-1 + xn-1,其中,x1 = 8,y1 = 1。当n>1时,x1指的是数的最高位,可以取5以外的1,2,3,4,6,7,8,9八个数中的一个,因为数的最高位没有0,所以x1不能取0。利用公式可以地推算出x2,y2,……,xn,yn
但是,当n=1时:
xn = 9,yn = 1

6、斐波那契数列

递推公式:

f[i]=f[i-1]+f[i-2],i>=2

如果i特别大,可以考虑使用矩阵快速幂的方法进行求解。

7、斯特林数

第一类斯特林数:

有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。

递推公式为:

S(n,0)=0 S ( n , 0 ) = 0

S(1,1)=1 S ( 1 , 1 ) = 1

S(n+1,k)=S(n,k−1)+nS(n,k) S ( n + 1 , k ) = S ( n , k − 1 ) + n S ( n , k )

第二类斯特林数:

是把包含n个元素的集合划分为正好k个非空子集的方法的数目。

递推公式为:

S(n,k)=0(n<kork=0) S ( n , k ) = 0 ( n < k o r k = 0 )

S(n,n)=S(n,1)=1 S ( n , n ) = S ( n , 1 ) = 1

S(n,k)=S(n−1,k−1)+kS(n−1,k) S ( n , k ) = S ( n − 1 , k − 1 ) + k S ( n − 1 , k )

例题:将n个有区别的求放入k个无标号的盒子中(盒子不能为空)的方案数

8、卡特兰数

前几项为$$1、1、2、5、14、42、132……
递推关系:

h(n) = C(2n,n)/(n+1) (n=1,2,3,……)

例题1:一个栈(无穷大)的进栈序列为1,2,3,……,你,有多少个不同的出栈序列。

例题2:一堆火车以严格的顺序到一个站里,问出来的时候有多少种顺序
输入:输入包含几个例子,每个例子包含一个正整数N(N小于等于100)
输出:输出所有的火车从车站里出来有多少种顺序。
样例输入:

1
2
3
10

样例输出:

1
2
5
16796

分析:由于结果会很大,需要用大数来解决,公式为:h(n) = h(n-1)*(4*n-2)/n+1.

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  rEZj93RghFYQ   2023年11月02日   26   0   0 i++leetcode-java
  bIizQVVwIKiD   2023年11月02日   41   0   0 递推#defineiOSgit
tU95LmXaBSF2
最新推荐 更多