题目链接

小红的暑假

题目描述

小红和她的三位朋友(编号为1, 2, 3)一共有 天暑假。她需要安排每天与其中一位朋友玩。最终的安排需要满足两个条件:

  1. 假期结束后,她与每位朋友都恰好玩了 天。
  2. 她不会连续两天和同一个朋友玩。

请求出满足条件的所有安排方案数,结果对 取模。

解题思路

这是一个典型的计数类动态规划问题。我们需要设计的状态要能够同时满足题目中的两个约束条件。

1. 状态定义

为了满足约束条件,我们的状态需要包含以下信息:

  • 已经和每位朋友玩了多少天(用于满足条件1)。
  • 前一天和哪位朋友玩的(用于满足条件2)。

因此,我们可以定义一个四维DP数组: 表示在已经过去的 天里,

  • 小红与朋友1玩了 天。
  • 小红与朋友2玩了 天。
  • 小红与朋友3玩了 天。
  • 并且,在第 天(也就是最后一天),她是和朋友 一起玩的。 其中

这个状态的含义是:满足上述条件时的总方案数。

2. 状态转移

我们考虑如何计算 。要想到达这个状态,我们必须从一个总天数为 的有效状态转移而来。

  • 计算 (最后一天和朋友1玩): 这种情况说明,在第 天,小红选择了朋友1。那么,在前 天里,她必然是和朋友1玩了 天,和朋友2、3分别玩了 天和 天。 同时,根据约束2,前一天的朋友不能是1,所以只能是朋友2或朋友3。 因此,方案数是前 天以朋友2或朋友3结尾的方案数之和。

  • 计算 (最后一天和朋友2玩): 同理,这种状态只能从前一天与朋友1或朋友3玩的状态转移而来。

  • 计算 (最后一天和朋友3玩): 同理,这种状态只能从前一天与朋友1或朋友2玩的状态转移而来。

3. 初始化 (Base Case)

在第一天,有三种可能:

  • 和朋友1玩:
  • 和朋友2玩:
  • 和朋友3玩: DP数组的其余所有值初始化为0。

4. 遍历顺序

我们从小到大枚举已经和各位朋友玩的天数。使用三层循环,分别遍历 从 0 到 。这样的顺序可以保证在计算 时,所有依赖的子问题(如 )都已经计算完毕。

5. 最终答案

我们的目标是求出 天的总方案数,其中和每位朋友都玩了 天。这对应于状态 。由于最后一天可以是和任意一位朋友玩,所以最终答案是这三种情况的总和:

代码

#include <iostream>
#include <vector>

using namespace std;

const int MOD = 1e9 + 7;

long long dp[101][101][101][4];

int main() {
    int k;
    cin >> k;

    dp[1][0][0][1] = 1;
    dp[0][1][0][2] = 1;
    dp[0][0][1][3] = 1;

    for (int i = 0; i <= k; ++i) {
        for (int j = 0; j <= k; ++j) {
            for (int l = 0; l <= k; ++l) {
                if (i == 0 && j == 0 && l == 0) continue;
                if (i == 1 && j == 0 && l == 0) continue;
                if (i == 0 && j == 1 && l == 0) continue;
                if (i == 0 && j == 0 && l == 1) continue;

                if (i > 0) {
                    dp[i][j][l][1] = (dp[i-1][j][l][2] + dp[i-1][j][l][3]) % MOD;
                }
                if (j > 0) {
                    dp[i][j][l][2] = (dp[i][j-1][l][1] + dp[i][j-1][l][3]) % MOD;
                }
                if (l > 0) {
                    dp[i][j][l][3] = (dp[i][j][l-1][1] + dp[i][j][l-1][2]) % MOD;
                }
            }
        }
    }

    long long ans = (dp[k][k][k][1] + dp[k][k][k][2] + dp[k][k][k][3]) % MOD;
    cout << ans << endl;

    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        int MOD = 1_000_000_007;

        long[][][][] dp = new long[k + 1][k + 1][k + 1][4];

        dp[1][0][0][1] = 1;
        dp[0][1][0][2] = 1;
        dp[0][0][1][3] = 1;

        for (int i = 0; i <= k; i++) {
            for (int j = 0; j <= k; j++) {
                for (int l = 0; l <= k; l++) {
                    if (i == 0 && j == 0 && l == 0) continue;
                    if (i == 1 && j == 0 && l == 0) continue;
                    if (i == 0 && j == 1 && l == 0) continue;
                    if (i == 0 && j == 0 && l == 1) continue;

                    if (i > 0) {
                        dp[i][j][l][1] = (dp[i-1][j][l][2] + dp[i-1][j][l][3]) % MOD;
                    }
                    if (j > 0) {
                        dp[i][j][l][2] = (dp[i][j-1][l][1] + dp[i][j-1][l][3]) % MOD;
                    }
                    if (l > 0) {
                        dp[i][j][l][3] = (dp[i][j][l-1][1] + dp[i][j][l-1][2]) % MOD;
                    }
                }
            }
        }

        long ans = (dp[k][k][k][1] + dp[k][k][k][2] + dp[k][k][k][3]) % MOD;
        System.out.println(ans);
    }
}
k = int(input())
MOD = 10**9 + 7

dp = [[[[0] * 4 for _ in range(k + 1)] for _ in range(k + 1)] for _ in range(k + 1)]

dp[1][0][0][1] = 1
dp[0][1][0][2] = 1
dp[0][0][1][3] = 1

for i in range(k + 1):
    for j in range(k + 1):
        for l in range(k + 1):
            if i == 0 and j == 0 and l == 0:
                continue
            # 跳过已经初始化的基础情况
            total_days = i + j + l
            if total_days == 1:
                continue

            if i > 0:
                dp[i][j][l][1] = (dp[i-1][j][l][2] + dp[i-1][j][l][3]) % MOD
            if j > 0:
                dp[i][j][l][2] = (dp[i][j-1][l][1] + dp[i][j-1][l][3]) % MOD
            if l > 0:
                dp[i][j][l][3] = (dp[i][j][l-1][1] + dp[i][j][l-1][2]) % MOD

ans = (dp[k][k][k][1] + dp[k][k][k][2] + dp[k][k][k][3]) % MOD
print(ans)

算法及复杂度

  • 算法:动态规划
  • 时间复杂度。我们需要填充一个三维的DP表(第四维是常数大小),填充每个状态的时间是
  • 空间复杂度。DP表的大小为