【好好补题,因为没准题目还会再出第三遍!!】ACM字符串-组合数学(官方题解是数位DP来写)

ACM字符串
1.长度不能超过n
2.字符串中仅包含大写字母
3.生成的字符串必须包含字符串“ACM”,ACM字符串要求连在一块!

ok,是不是很简单?现在告诉你n的值,你来告诉我这样的字符串有多少个

输入

输入一个正整数T,代表有T组数据 
接下来T行,每行一个正整数n,n<=10。 

输出

输出符合条件的字符串的数目

样例输入

1
3


样例输出

1

做题过程:

  1. 熬了三四个小时,WA了无数次!最终推出了组合数的公式!
  2. 首先暴力打表,嘿嘿!这样极大地压缩计算时间!
  3. 打表如下:

    一:生成连续的7位绝对不含ACM的数据的个数!

    ll Els[10];//生成7位绝对不含ACM的数据
    int a[10];
    
    void dfs(int now,int len)
    {
        if(now>len)
        {
            Els[len]++;
            return ;
        }
        for( int i=1; i<=26; i++)
        {
            a[now]=i;
            if(now>=3&&a[now]==3&&a[now-1]==2&&a[now-2]==1)
                continue;
            dfs(now+1,len);
        }
    }
    int main()
    {
    
        for(int i=1; i<=3; i++)
        {
            dfs(1,i);
            printf("i=%d, %lld\n",i,Els[i]);
        }
    
        cout<<"生成连续的7位绝对不含ACM的数据"<<endl;
        for(int i=1; i<=3; i++)
        {
            printf("i=%d, %lld\n",i,Els[i]);
        }
    
        return 0;
    }

     

  4. 二:开始进行组合数学处理,并且处理误差!

  5. 误差是怎么回事呢?举个栗子当“n=6时,形如当只含有一个ACM时—— “ACM_ _ _”这种样子C(4,1)种组合数(把ACM捆绑成一块!),然后需要C(4,1) 乘于其余三个空位上的数字组合,这三种数字“因为不能再含有ACM”直接调用上面求出的Els[3], 但是这里存在了误差!
  6. 仔细考虑一下,当出现“_ACM_ _”的组合的这种情况时,其实有Els[1]*Els[2] 种!也就是说中间的“ACM”隔开了两边,我上面求的那么“Els”的真实意思是“生成连续的7位绝对不含ACM的数据个数”!Els[1]*Els[2] -Els[3] = 1 ,这里的误差就是1!以此类推!
  7. 同理,其他的情况也是如此!多的位数可以跑循环进行枚举!手写就有点麻烦了!
#define ll long long
ll F[12]; //n的阶乘
//ll f[12];
                ///chart表示连续的绝对不含ACM连续的字符串个数,就是上面求出的"Els 数组"
ll chart[12]={1,  26,676,17575,  456924,11879348,308845473,  8029525374};
ll mistake[12];    ///存储每个长度n的误差!
ll Cul(int m,int k)  //计算C(m,k)的组合数
{
    return F[m]/(F[k]*F[m-k] );
}
ll ans[12]={0,0,0,1};
void init(){
    F[0]=1;
    for(int i=1;i<=10;i++)  //n的阶乘!
        F[i]=F[i-1]*i;
    memset(mistake,0,sizeof(mistake));
    mistake[6]+= chart[1]*chart[2]*2- 2*chart[3];
    mistake[7]+= chart[1]*chart[3]*2+chart[2]*chart[2]- 3*chart[4];
    mistake[8]+= chart[1]*chart[4]*2+chart[2]*chart[3]*2- 4*chart[5];
    mistake[9]+= chart[1]*chart[5]*2+chart[2]*chart[4]*2+chart[3]*chart[3] - 5*chart[6];
    mistake[9]+= chart[1]*chart[2]*6 +chart[1]*chart[1]*chart[1] - chart[3]*7;

    for(int i=1;i<=7;i++)  ///__A_____
        mistake[10]+=chart[i]*chart[7-i]-chart[7];
    for(int j=0;j<=4;j++){
        for(int i=0;i+j<=4;i++)  ///_A__A__(空出j个位置和i个位置和4-i-j个位置!!)
            mistake[10]+=chart[j]*chart[i]*chart[4-i-j]-chart[4];
    }


}
int main(){

    init();

    for(int j=4;j<=10;j++)
    {
       int n=j;
        int num=n/3;
         ans[j]=ans[j-1];
        for(int i=1;i<=num;i++){  //单个的,结果需要累加!!
            ans[j]+=Cul(i+n-i*3,i)*chart[n-i*3] ;
        }
      //   printf("j=%d, %lld\n",j,ans[j]);
        ans[j]+= mistake[j];
     //   printf("+mis::  j=%d, %lld\n",j,ans[j]);
    }

    int T;
    scanf("%d",&T);

    while(T--){
        int n;
        cin>>n;

        printf("%lld\n",ans[n]);
    }

    return 0;
}

 

官方题解是数位DP来写,数位DP其实就是记忆化搜索+深搜!建议学学!

 

posted @ 2018-09-22 19:26  山枫叶纷飞  阅读(284)  评论(0编辑  收藏  举报