2019第四次新生周赛——YZJ的牛肉干

本文详细解析了一个ACM集训队的趣味数学问题——牛肉干刻字,探讨如何在牛肉干上刻下由YZJ三种字符组成的字符串,且Z字符不能相邻的计数方法。文章提供了两种解决方案,一种是递推公式,另一种是使用矩阵快速幂算法,适用于大规模数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述

今年的ACM暑期集训队一共有18人,分为6支队伍。其中有一个叫做YZJ的大佬,在共同的集训生活中,大家建立了深厚的友谊,YZJ准备做点什么来纪念这段激情燃烧的岁月,想了一想,YZJYZJ从家里拿来了一块上等的牛肉干,准备在上面刻下一个长度为nn的只由"Y" "Z" "J"三种字符组成的字符串(可以只有其中一种或两种字符,但绝对不能有其他字符),YZJ学长同时禁止在串中出现ZZ相邻的情况,他认为,"ZZ"看起来就像骂人的话语,影响不好。

你,NEW ACMer,YZJ的崇拜者,能帮学长算一下一共有多少种满足要求的不同的字符串吗?

输入

输入数据包含多个测试实例,每个测试实例占一行,由一个整数n组成,(0<n<40)。

输出

对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。

输入样例 1 

1
2

输出样例 1

3
8

来源

hg

在牛肉干上面刻字,刻字的选择是Y,Z,J三个字符中的任意一个。

限制条件:Z和Z不能相连。

接下来,画图找规律:(图省略了~~~)

1      f[1]=3

2      f[2]=8

3      f[3]=22

4      f[4]=60

突然发现f[n]=2*(f[n-1]+f[n-2])。

#include<cstdio>
const int maxn=50;
long long a[maxn]={1,3};
int main()
{
	int i,n;
	for(i=2;i<40;i++)
	{
		a[i]=2*(a[i-1]+a[i-2]);
	}
	while(~scanf("%d",&n))
	{
		printf("%lld\n",a[n]);
	}
	return 0;
}

当然,如果说这道题的n的数据范围特别大的话,这样做肯定是不行的(爆数据范围),可以使用矩阵快速幂的方法来解决这个问题。

矩阵的构造:

现在是三个字符Y,Z,J,其中Z和Z不能相邻,那么所有的清苦应该有八种:

YY、YZ、YJ、ZY、ZJ、JY、JZ、JJ,将这八种情况用从0开始的数字表示,对应的数字分别为0、1、2、3、4、5、6、7.

对于YY来说,确定了第一个Y之后,它对后面的情况没有影响了,所以只需考虑后面的Y就行了,也就是YY,YZ,YJ这三种情况。

对应的数字分别为0,1,2.

同理找出其他的关系,然后构造矩阵:

代码如下:
 

#include<algorithm>
#include<iostream>
#include<string.h>
#include<cstdio>
#include<cstring>
#define  P pair<long long,long long>
#define ms(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
ll N,a,b,k;

struct xiao
{
    ll f[8][8];
} ans,mapp;

void init(xiao &ans)
{
    for(int i=0; i<8; ++i)
    {
        for(int j=0; j<8; ++j)
        {
            if(i==j)
                ans.f[i][j]=1;
            else
                ans.f[i][j]=0;
        }
    }
}

void power(xiao &x,xiao &y,xiao &z)
{
    ms(z.f,0);
    for(int i=0; i<8; ++i)
    {
        for(int j=0; j<8; ++j)
        {
            if(x.f[i][j])
            {
                for(int k=0; k<8; ++k)
                {
                    z.f[i][k]+=(x.f[i][j]*y.f[j][k]);
                }
            }
        }
    }
}

ll solve(ll k)
{
    init(ans);
    ll number=k;
    xiao temp=mapp,t;
    while(number)
    {
        if(number%2==1)
        {
            power(ans,temp,t);
            ans=t;
        }
        power(temp,temp,t);
        temp=t;
        number/=2;
    }
    return ans.f[0][0]+ans.f[0][1]+ans.f[0][2]+ans.f[0][3]+ans.f[0][4]+ans.f[0][5]+ans.f[0][6]+ans.f[0][7];
}


int main()
{
    ll n;
    mapp=
    {
        1,1,1,0,0,0,0,0,
        0,0,0,1,1,0,0,0,
        0,0,0,0,0,1,1,1,
        1,1,1,0,0,0,0,0,
        0,0,0,0,0,1,1,1,
        1,1,1,0,0,0,0,0,
        0,0,0,1,1,0,0,0,
        0,0,0,0,0,1,1,1
    };
    xiao t,x;
    power(mapp,mapp,t);
    while(cin>>n)
    {
        printf("%lld\n",solve(n));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值