POJ 1850 code(组合数学)

POJ 1850 code

题目大意

将字母和单词(全部小写并且按照字典序递增)按照字典序编号,比如
a-1
b-2

z-26
ab-27

az-51

给你一个字母或单词,问你它的编号

分析

以树状的结构来分析这道题会比较直观。
这里写图片描述
按照树的节点依次编号,每个字母或单词就落在一个节点上。要求一个单词的编号,比较直接的想法是分别求出该 单词上一层的最大编号和该单词在当前层中所处的位置。(根节点为第0层)

下面分别进行讨论

  • 上一层的最大编号

    某一层的节点数是有规律可循的,而某一层的最大编号则是该层及之前所有层的节点数之和。

    令第i层的节点数为 Fi

    数学公式
    Cmn=n1i=1Cm1i(1)

    F1 =26

    F2 =25+24+…+1= 25i=1i = C226 = C125+C124+...+C11 = 25i=1C1i

    F3 = 24i=1(ij=1C1j) = 24i=1C2i+1 = C326

    同理 F4 = C426 (求出前三个第四个也可以猜出来了)

    某一层k的最大编号则是 F1+F2+...+Fk ,找不到化简公式也可以直接用循环直接求出来。

  • 当前层中所处的位置

    求在当前层中所处的位置则要复杂一些了,看似不是规则的树状结构,但仔细观察后发现这棵树是具有自相似性的,比如以编号为1的节点为根节点的子树具有和原树一样的性质。基于这一条启发式信息,问题就可以求解了.

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
const int INF=999999999;
#define LL long long int
char s[11];//输入
int bj[30];//标记之前某个字母是否被访问过
LL f[27][11];//f[i][j]表示对于根节点有i个分支第j层的节点数, 根节点是第0层
LL A[11];
int Find_loc(char c0,char c,int bj[])//在某些字母被标记的情况下询问字母c处于字母树中的第几个分支,c0是前一个字母
{
     int cnt=0;
     for(int i=(int)c0-96+1;i<=26;i++)
     {
         if(bj[i]==0)cnt++;
         if(i==(int)c-96){return cnt;}
     }
}
void Get_A()
{
      LL t=1;
      for(int i=1;i<=10;i++){t=t*i;A[i]=t;}
}
void Get_f()
{
    LL x;
    for(int i=1;i<=26;i++)
    {
          f[i][0]=1;
          x=1;//保存当前层的节点数
          for(int j=1;j<=min(10,i);j++)
          {
                x=(i-j+1)*x;
                f[i][j]=x/A[j];
          }
    }
}
bool Check(char s[])
{
      int lenth=strlen(s);
      if(lenth>10)return 0;
      for(int i=1;i<lenth;i++)if(s[i]<s[i-1])return 0;
      return 1;
}
void Work()
{
      LL ans=0;
      int lenth=strlen(s);
      if(Check(s)==0){cout<<0<<endl;return ;}
      int loc;
      LL node=26;//保存当前的节点的父亲的分支数
      for(int i=1;i<=lenth-1;i++)ans+=f[26][i];//求上一层的最大编号
      //cout<<"before_ans: "<<ans<<endl;
      for(int i=0;i<lenth;i++)
      {
            if(i==0)loc=(int)s[i]-96;
            else loc=Find_loc(s[i-1],s[i],bj);
            bj[(int)s[i]-96]=1;
            for(int j=1;j<loc;j++)ans+=f[node-j][lenth-i-1];
            node=node-loc;//更新node
      }
      ans+=1;
      cout<<ans<<endl;
}
int main()
{
    Get_A();
    Get_f();
    while(scanf("%s",s)!=EOF)
    {
          Work();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值