组合数

1|0组合数

1|1基本做法

先看看不做预处理计算一个组合数:

int C(int r, int n) { int ans = 1; for (int i = 1; i <= r; i++) { ans *= n - i + 1; ans /= i; } return ans; }

1|2预处理组合数

针对大多数仅仅是利用组合数求解问题的题目运用递推法打表,不仅方便,而且可以稳稳地控制复杂度,对于需要多次引用组合数的题目效果极佳:

基于组合数公理性质:

Cnm=Cnnm

推得:

Cnm=Cn1m1+Cn1m

由这个递推公式就可以熟练的写出组合数代码,但要注意初始化:

C00=0

C0i=C01=C11=1

同时,把表打出来后,我们会发现这就是杨辉三角,这个三角可以解决很多问题,记住打印三角的方法也可以打出组合数。

inline void build() { c[0][0]=1; c[1][0]=c[1][1]=1;//如上初始化,绝对绝对不能忘记或错,结合常识。 for(int i=2;i<=2000;i++) { c[i][0]=1; for(int j=1;j<=2000;j++) c[i][j]=c[i-1][j-1]+c[i-1][j];//递推公式。 } }

1|3例题

P2822 [NOIP2016 提高组] 组合数问题

这题还需要再用前缀和来优化查询速度
前缀和可以有效减少查询统计时的复杂度,每一次查询O(n)降到O(1)
记住:上加左 减左上 加自己
ans[i][j]=ans[i][j1]+ans[i1][j]ans[i1][j1]

inline void build() { c[0][0]=1; c[1][0]=c[1][1]=1; for(int i=2;i<=2000;i++) { c[i][0]=1; for(int j=1;j<=i;j++) { c[i][j]=(c[i-1][j-1]+c[i-1][j])%k; ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];//前缀和。 if(!c[i][j])ans[i][j]++;//如果满足结论,计数加一。 } ans[i][i+1]=ans[i][i];//继承。 } } inline void solve() { t=read(),k=read(); build(); while(t--) { n=read(),m=read(); if(m>n)printf("%lld\n",ans[n][n]);//如果m>n,ans只会达到n,只需输出ans[n,n]就可以了。 else printf("%lld\n",ans[n][m]); } }
  1. 需掌握组合数的基本两种求解方法(通项公式,递推公式),根据数据范围选定方法。
  2. 结合数据范围找到优化方法,无论是取模还是自己的玄学优化都尝试一下.(建议取模输出的题,绝对不要乱模!既费时又易错)
  3. 掌握前缀和,利用前缀和对降维的作用。

P1246 编码

cgx 举例,设 ans 为比 cgx 小的单词个数,初值为 0

1. 首先,cgx 肯定比只有一个字母的单词大,ans+26,ans=26

2. 其次,cgx 肯定比只有两个字母的单词大。

只有两个字母的单词个数该怎么算呢?就是在 26 个字母中选 2 个。

ans+C262,C262=262521=325,ans=26+325=351

3. 只有三个字母

3.1. 第一位:它比以字母 a-b 为第一位的大( cgx 第一位为 c,所以要比 c 小)

  • a 为第一位,且有三位的单词个数:C252=252421=300

    a 大的剩下 25 个字母中选 2 个字母:
    ans+300,ans=351+300=651

  • b 为第一位,且有三位的个数:C242=242321=276

    b 大的剩下 24 个字母中选 2 个字母:ans+276,ans=651+276=927

3.2 第二位(即第一位已经确定为 c):它比以字母 d-f 为第二位的单词大 (第一位为 c ,所以要比 c 大,第二位为 g,所以要比 g 小)

  • 第二位为 d 的个数:C221=221=22

    从比 d 大的剩下 22 个字母中选 1 个字母:ans+22,ans=927+22=949

  • 第二位为 e 的个数:C211=21,ans+21,ans=949+21=970

  • 第二位为 f 的个数:C201=20,ans+20,ans=970+20=990

3.3 第三位(一,二位已经确定):它比以字母 h-w 为第三位的大,共有 16 个。

n=823Cn0=n=8231=161=16

ans+16,ans=990+16=1006

因为比 cgx 小的单词共有 1006 个,所以 cgx 是第 1007 个。


先判断该单词是否存在,再一位一位地去考虑共有多少比给出单词小的单词,如果用的是字符串,则还需要考虑一下边界问题。

#include<bits/stdc++.h> using namespace std; string s; int ans,n; int c(int m,int n)//组合数计算 { if(m==0)return 1; int mut=1; for(int i=n;i>n-m;i--)mut*=i; for(int i=m;i>1;i--)mut/=i; return mut; } int main() { cin>>s,n=s.size(); for(int i=1;i<n;i++) if(s[i]<=s[i-1])cout<<0,exit(0);//判断是否存在 for(int i=1;i<n;i++)ans+=c(i,26);//计算出位数比它小的单词数 for(int i=0;i<n;i++)//枚举每一位 for(char j=(i==0?'a':s[i-1]+1);j<s[i];j++)//注意考虑边界 ans+=c(n-i-1,'z'-j);//因为字符串下标从0开始,所以是n-i-1而不是n-i cout<<++ans;//别忘了最后把自己加上 return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17871761.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示