题意

给定正整数数N,求把N分割成一些正整数a1,a2......ak的方案总数,其中k是偶数且a2i-1=4

30分做法:暴力搜索

class Solution {
public:
    const int MOD=998244353;
    int messageCount(int N) {
        return dfs(N);
    }
    int dfs(int t)
        {
            int ans=0;//初始化答案
            if(t==0) return 1;//若分割成功答案+1
            if(t<=4) return 0;//若分割不成功答案不变
            for(int i=1;i<=t-4;++i) {ans+=dfs(t-4-i);ans%=MOD;}//递归计算答案
        return ans;
        }
};//时间复杂度O(N!)

直接深度搜索每一种方案并返回总方案数,若分割成功,将方案数加1,最后计算总方案数。
时间复杂度:,当dfs到第N层时,需要再次dfs从N-4到1,故时间复杂度为
空间复杂度:,每次dfs所用栈空间为级别。

60分做法:记忆化搜索

class Solution {
public:
    const int MOD=998244353;
    int a[1000005];
    int messageCount(int N) {
        memset(a,0,sizeof a);
        return dfs(N);
    }
    int dfs(int t)
        {
        if(a[t]) return a[t];//如果已经搜索过了直接返回,避免重复搜索
             int ans=0;//初始化答案
            if(t==0) return 1;//若分割成功答案+1
            if(t<=4) return 0;//若分割不成功答案不变
            for(int i=1;i<=t-4;++i) {if(!a[t-4-i])a[t-4-i]=dfs(t-4-i);ans+=a[t-4-i];ans%=MOD;}//存储已经计算的答案
        return ans;
        }
};//时间复杂度O(N^2)

a数组用于储存已经计算的dfs函数,节省了大量时间,但搜索仍不是正解
时间复杂度:,对于每个N只会进行一次搜索,每个N平均搜索N/4次,时间复杂度约为平方级别。
空间复杂度:,从1到n每个数都跑一次dfs,每次dfs复杂度约为,故空间复杂度为

100分做法:动态规划

class Solution {
public:
    const int MOD=998244353;
    int messageCount(int N) {
        int dp[1000005];
        memset(dp,0,sizeof dp);
        dp[5]=1;
        for(int i=6;i<=N;i++)
            dp[i]=(dp[i-1]+dp[i-5])%MOD;
        return dp[N];
    }
};//时间复杂度O(N)

本题的正解应当为动态规划。观察到后面数字的变化不影响之前数字的规律成立,考虑动态规划。
假设 指消息长度为N时的划分数,容易知道图片说明 。下面证明一下该递推公式:
对于消息长度为N-1的情况,在划分出的数中最后一个数加1(例如4 1 4 2->4 1 4 3),仍是一个合法数列,对于消息长度为N-5的情况,在划分出的数中加入4,1两个数,也仍是一个合法数列(例如4 1 4 2->4 1 4 2 4 1),前者遍历了所有最后一个数大于1的方案,后者遍历了所有最后一个数为1的方案,两者合起来就是指消息长度为N时的划分数。

而当N=5时,方案只有一种,故可以从一直推算出图片说明 。代码中只有一个循环,时间复杂度为图片说明
空间复杂度:,为数组的空间开销。

图为已知计算的过程,可以看到是由经过末尾加入4,1变成的,经过最后一个数字加1得来的。