牛客网288555题解题指南:动态规划求解小红的暑假(附代码解析)
一、题目解读
牛客网288555题要求解决一个组合数学问题:有三位朋友,每天需邀请其中一位参加聚会,但不能连续两天邀请同一位朋友。给定天数n,求满足条件的不同邀请方案总数。题目考察动态规划、状态转移及组合计数,属于中等难度算法题。
二、解题思路:动态规划 + 状态压缩
核心思想是将问题分解为子问题,利用动态规划记录中间状态。定义四维dp数组:dp[a][b][c][last],表示已邀请朋友1 a次、朋友2 b次、朋友3 c次,且最后一天邀请的是last(1/2/3)的方案数。通过状态转移方程,枚举下一位朋友的选择,避免重复计数。
三、解题步骤
1. 初始化:第一天可选任意朋友,初始化dp[1][0][0][1]、dp[0][1][0][2]、dp[0][0][1][3]为1。
2. 状态转移循环:遍历所有可能的(a,b,c)组合,若当前状态dp[a][b][c][last]有效:
枚举下一个朋友next(1/2/3),跳过与last相同的情况。
更新次数:na=a+(next=1), nb=b+(next=2), nc=c+(next=3)。
若次数不超n,累加方案数:dp[na][nb][nc][next] += dp[a][b][c][last]。
3. 结果计算:最终方案为所有朋友均被邀请n次的状态总和,即dp[n][n][n][1] + dp[n][n][n][2] + dp[n][n][n][3](取模防止溢出)。
四、代码与注释
#include <iostream> #include <vector> #include <cstring> using namespace std; const int MOD = 1e9 + 7; // 取模常数,防止结果溢出 const int MAX_N = 100; // 最大天数限制 // dp[a][b][c][last] 表示选了a次朋友1,b次朋友2,c次朋友3,最后选的是last的方案数 int dp[MAX_N+1][MAX_N+1][MAX_N+1][4]; int main() { int n; cin >> n; // 输入天数n memset(dp, 0, sizeof(dp)); // 初始化dp数组为0 // 初始状态:第一天可选任意朋友 dp[1][0][0][1] = 1; // 选朋友1一次,最后为1 dp[0][1][0][2] = 1; // 选朋友2一次,最后为2 dp[0][0][1][3] = 1; // 选朋友3一次,最后为3 for (int a = 0; a <= n; ++a) { for (int b = 0; b <= n; ++b) { for (int c = 0; c <= n; ++c) { if (a + b + c == 0) continue; // 跳过全0状态(无效) for (int last = 1; last <= 3; ++last) { if (dp[a][b][c][last] == 0) continue; // 当前状态无效则跳过 // 尝试选择下一个朋友 for (int next = 1; next <= 3; ++next) { if (next == last) continue; // 避免连续选同一人 int na = a, nb = b, nc = c; if (next == 1) na++; // 更新次数 else if (next == 2) nb++; else nC++; if (na <= n && nb <= n && nc <= n) { // 累加方案数(取模) dp[na][nb][nc][next] = (dp[na][nb][nc][next] + dp[a][b][c][last]) % MOD; } } } } } } // 最终结果是所有朋友都被选n次的总和 int result = 0; for (int last = 1; last <= 3; ++last) { result = (result + dp[n][n][n][last]) % MOD; // 取模求和 } cout << result << endl; return 0; }
五、总结
本题通过动态规划巧妙解决组合限制问题,关键在于四维状态设计(次数+末尾选择)和转移条件的严谨性。优化点包括利用取模运算避免大数溢出,以及跳过无效状态提升效率。掌握此类状态压缩技巧,可应对更多复杂计数问题。
原创内容 转载请注明出处