组合数学总结

本文介绍了两个排列组合问题:Jam计数法中如何找到给定Jam数字后面的5个数字,以及计算组合数Cnk的因子个数。通过将Jam数字转化为数组并寻找可替换字母,以及对组合数进行质因数分解,可以解决这两个问题。
摘要由CSDN通过智能技术生成
问题 E: 【排列组合问题】Jam的计数法

题目描述

囚犯之间的秘密交流使用一种被称为Jam的计数法,这种计数法不使用阿拉伯数字计数,而是使用小写英文字母计数,每个数字的位数都是相同的(使用相同个数的字母),英文字母按原先的顺序,排在前面的字母小于排在它后面的字母。在 Jam数字中,每个字母互不相同,而且从左到右是严格递增的。每次,Jam计数法还指定使用字母的范围,例如,从2到10,表示只能使用{b,c,d,e,f, g,h,i,j}这些字母。如果再规定位数为5,那么,紧接在Jam数字“bdfij”之后的数字应该是“bdghi”。(如果用U、V依次表示 Jam数字“bdfij”与“bdghi”,则U<V,且不存在Jam数字P,使U<P<V)。作为联络员的邪狼,他的任务是:对于从文件读入的一个 Jam数字,按顺序输出紧接在后面的5个Jam数字,如果后面没有那么多Jam数字,那么有几个就输出几个。

输入

有2行,第1行为3个正整数,用一个空格隔开:s t w(其中,s为所使用的最小的字母的序号,t为所使用的最大的字母的序号。w为数字的位数,这3个数满足:1≤s<t≤26,2≤w≤t-s )
第2行为具有w个小写字母的字符串,为一个符合要求的Jam数字。

输出

最多为5行,为紧接在输入的Jam数字后面的5个Jam数字,如果后面没有那么多Jam数字,那么有几个就输出几个。每行只输出一个Jam数字,是由w个小写字母组成的字符串,不要有多余的空格。

样例输入 Copy

2 10 5
bdfij

样例输出 Copy

bdghi
bdghj
bdgij
bdhij
befgh

思路:
本题是字母序列的变换,我将其转化成数组来处理更为方便,转化的方程a[i]=c-‘a’+1;(c为输入的字母)
1)首先需要找到可以替换的字母,可替换的字母满足不等式a[i]!=endd+i-len(即替换该字母和其之后的字母之后,字母序列中的每一个字母都在范围[s,t]内,又因为本题的序列是递增的,所以从最后一个字母找起
2)找出替换的字母之后,将其修改成原来的字母的后一个字母,然后将其后的字母以公差为1递增排列。(可以证明这样排列是最小的)

#include <iostream>

using namespace std;

int a[30];
int start,endd,len;
int check(int a[])///找到可以改变的哪个字母
{
    for(int i=len;i>=1;i--)
        if(a[i]!=endd+i-len) return i;
    return 0;
}

void change(int *a,int sign)///对其和其之后的字母进行修改
{
    a[sign]++;
    for(int i=sign+1;i<=len;i++)
        a[i]=a[i-1]+1;
}

int main()
{
    cin>>start>>endd>>len;
    char c;
    for(int i=1;i<=len;i++)
    {
        cin>>c;
        a[i]=c-'a'+1;
    }
    for(int i=1,sign=check(a);i<=5&&sign;i++,sign=check(a))
    {///int sign=check(a);不能放循环中的初始化部分,这样只执行一次
        change(a,sign);
        for(int j=1;j<=len;j++)
            cout<<char('a'+a[j]-1);///a[i]写成了a[len]
        cout<<endl;
    }
    return 0;
}

问题 F: 【排列组合问题】Divisors

题目描述

Your task in this problem is to determine the number of divisors of Cnk. Just for fun – or do you need any special reason for such a useful computation?

输入

The input consists of several instances. Each instance consists of a single line containing two integers n and k (0 ≤ k ≤ n ≤ 431), separated by a single space.

输出

For each instance, output a line containing exactly one integer – the number of distinct divisors of Cnk. For the input instances, this number does not exceed 263 - 1.

样例输入 Copy

5 1
6 3
10 4

样例输出 Copy

2
6
16

思路:
C(m,n)=m!/(m-n)!/n!,求其因子个数,先对m!进行质因数分解,分解方式为getDivisor函数,求出m!中含有dsum的因子,除以(m-n)!中含有的 dsum1和n!中含有的dsum2,即d(sum-sum1-sum2),然后使用求因子个数的方法,即可计算出结果。

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

int prime[450];
int cnt=1;
void getprime()///找出小于等于432的质数
{
    for(int i=2;i<=432;i++)
    {
        int flag=0;
        for(int j=2;j<i;j++)
            if(i%j==0) flag=1;
        if(!flag) prime[cnt++]=i;
    }
    cnt--;
}

ll getDivisor(int n,int d)///求出n!中含有d^sum因子
{
    if(d>n) return 0;///如果d>n,则n!不含因子d,即sum=0(d^sum=1)
    int sum=0;
    while(n)
    {
        sum+=n/d;
        n/=d;
    }
    return sum;
}

int main()
{
    getprime();
    int m,n;
    while(cin>>m>>n)
    {
        int k=m-n;
        long long sum=1;
        for(int i=1;prime[i]<=m;i++)///遍历小于m的质因子,求出含有因子prime[i]的次数
            sum=sum*(getDivisor(m,prime[i])-getDivisor(n,prime[i])-getDivisor(k,prime[i])+1);
        cout<<sum<<endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值