小红的暑假
[题目链接](https://www.nowcoder.com/practice/6770931d7bb74c29863c23c375907c33)
思路
小红和三位朋友共有 天暑假,每天选一位朋友玩,最终每位朋友恰好玩了
天。要求不存在连续两天找同一个朋友。问满足条件的方案数,结果对
取模。
动态规划——逐天填充
将三位朋友编号为 。我们逐天决定当天和谁玩,用 DP 跟踪已经和每位朋友玩了多少天,以及昨天找的是谁。
设 表示:已经和朋友 0 玩了
天、和朋友 1 玩了
天(则和朋友 2 玩了
天,其中
是当前总天数),且最后一天找的是朋友
的方案数。
转移:在第 天,可以选择任意一位不等于
的朋友
,前提是该朋友的已玩天数尚未达到
:
- 选朋友 0(
且
):
- 选朋友 1(
且
):
- 选朋友 2(
且
,其中
):
初始状态:第 1 天可以选择任意朋友,即 。
答案:。
优化遍历范围
对于第 天的状态
,需满足
、
、
,即
且
。通过精确计算循环上下界,只遍历有效状态,避免无效访问。使用滚动数组(交换 dp 和 ndp)节省空间。
复杂度分析
- 时间复杂度:
。总共
步,每步遍历的有效
对数量在中间步骤最多为
,总计
。
- 空间复杂度:
,两个
的数组。
代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int MAXN = 1002;
long long A[MAXN][MAXN][3], B[MAXN][MAXN][3];
int main() {
int n;
scanf("%d", &n);
if (n == 0) { puts("1"); return 0; }
auto dp = A, ndp = B;
dp[1][0][0] = dp[0][1][1] = dp[0][0][2] = 1;
for (int t = 2; t <= 3 * n; t++) {
int imin = max(0, t - 2 * n), imax = min(n, t);
for (int i = imin; i <= imax; i++) {
int jl = max(0, t - i - n), jr = min(n, t - i);
for (int j = jl; j <= jr; j++)
ndp[i][j][0] = ndp[i][j][1] = ndp[i][j][2] = 0;
}
int pi0 = max(0, t - 1 - 2 * n), pi1 = min(n, t - 1);
for (int i = pi0; i <= pi1; i++) {
int pj0 = max(0, t - 1 - i - n), pj1 = min(n, t - 1 - i);
for (int j = pj0; j <= pj1; j++) {
int k = t - 1 - i - j;
long long v0 = dp[i][j][0], v1 = dp[i][j][1], v2 = dp[i][j][2];
if ((v0 | v1 | v2) == 0) continue;
if (i + 1 <= n) ndp[i+1][j][0] = (ndp[i+1][j][0] + v1 + v2) % MOD;
if (j + 1 <= n) ndp[i][j+1][1] = (ndp[i][j+1][1] + v0 + v2) % MOD;
if (k + 1 <= n) ndp[i][j][2] = (ndp[i][j][2] + v0 + v1) % MOD;
}
}
swap(dp, ndp);
}
printf("%lld\n", (dp[n][n][0] + dp[n][n][1] + dp[n][n][2]) % MOD);
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
final int MOD = 1_000_000_007;
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
if (n == 0) { System.out.println(1); return; }
int m = n + 2;
long[][][] dp = new long[m][m][3], ndp = new long[m][m][3];
dp[1][0][0] = dp[0][1][1] = dp[0][0][2] = 1;
for (int t = 2; t <= 3 * n; t++) {
int imin = Math.max(0, t - 2 * n), imax = Math.min(n, t);
for (int i = imin; i <= imax; i++) {
int jl = Math.max(0, t - i - n), jr = Math.min(n, t - i);
for (int j = jl; j <= jr; j++)
ndp[i][j][0] = ndp[i][j][1] = ndp[i][j][2] = 0;
}
int pi0 = Math.max(0, t - 1 - 2 * n), pi1 = Math.min(n, t - 1);
for (int i = pi0; i <= pi1; i++) {
int pj0 = Math.max(0, t - 1 - i - n), pj1 = Math.min(n, t - 1 - i);
for (int j = pj0; j <= pj1; j++) {
int k = t - 1 - i - j;
long v0 = dp[i][j][0], v1 = dp[i][j][1], v2 = dp[i][j][2];
if ((v0 | v1 | v2) == 0) continue;
if (i + 1 <= n) ndp[i+1][j][0] = (ndp[i+1][j][0] + v1 + v2) % MOD;
if (j + 1 <= n) ndp[i][j+1][1] = (ndp[i][j+1][1] + v0 + v2) % MOD;
if (k + 1 <= n) ndp[i][j][2] = (ndp[i][j][2] + v0 + v1) % MOD;
}
}
long[][][] tmp = dp; dp = ndp; ndp = tmp;
}
System.out.println((dp[n][n][0] + dp[n][n][1] + dp[n][n][2]) % MOD);
}
}

京公网安备 11010502036488号