P8395 [CCC 2022 S1] Good Fours and Good Fives

记录87

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int dp[N];
int main(){
	int n;
	cin>>n;
	dp[0]=1;// 开始的第一个方案,以它为迭代
	for(int i=4;i<=n;i+=4) dp[i]+=dp[i-4];
	for(int i=5;i<=n;i++) dp[i]+=dp[i-5];
	cout<<dp[n];
	return 0;
} 

题目传送门https://www.luogu.com.cn/problem/P8395


突破口

14=5+5+4

20=4+4+4+4+4 或 20=5+5+5+5

40=4+4+4+4+4+4+4+4+4+4 或 40=4+4+4+4+4+5+5+5+5 或 40=5+5+5+5+5+5+5+5

当然,4 和 5 的顺序并不重要,重要的是他们的个数。

给你一个正整数 n,问有多少种方法可以用 4 和 5 拼凑成 n。


思路

🎯 问题本质

给定一个正整数 n,问有多少种非负整数解 (x, y) 满足:

4x+5y=n4x+5y=n

其中 x 是 4 的个数,y 是 5 的个数。
顺序不重要 → 只关心用了多少个 4 和多少个 5,不关心排列顺序。

✅ 这是一个完全背包计数问题

物品:4 和 5(每种无限个)

背包容量:n

问:恰好装满背包的方案数?


🧠 动态规划设计(完全背包)

状态定义
  • dp[i] 表示用 4 和 5 恰好拼出 i 的方案数
初始状态
  • dp[0] = 1:拼出 0 有一种方案 ------ 什么都不选
状态转移
  • 先考虑只用 4
    dp[i] += dp[i - 4](如果 i ≥ 4)
  • 再考虑加入 5
    dp[i] += dp[i - 5](如果 i ≥ 5)

⚠️ 注意:必须按"物品"顺序处理 ,避免重复计数!

正确做法是:外层枚举物品,内层枚举容量(完全背包标准写法)

但本题只有两个物品(4 和 5),所以可以分两步:

  1. 先用 4 更新所有能被 4 拼出的数
  2. 再用 5 去更新(在已有基础上加 5)

这样就能保证:每个方案只按"先 4 后 5"的方式计数一次,不会重复。


代码解析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int dp[N];
  • 定义 dp 数组,大小 1e6+10(满足 n ≤ 1e6)
cpp 复制代码
int main(){
    int n;
    cin >> n;
cpp 复制代码
    dp[0] = 1; // 基础:拼出 0 有 1 种方案(空方案)

🔹 第一步:只用数字 4

cpp 复制代码
    for(int i = 4; i <= n; i += 4)
        dp[i] += dp[i - 4];
  • 从 4 开始,每次加 4
  • dp[4] += dp[0] = 1dp[4] = 1
  • dp[8] += dp[4] = 1dp[8] = 1
  • ...
  • 结果:所有 i % 4 == 0 的位置 dp[i] = 1,其余仍为 0

✅ 这表示:仅用 4 能拼出的数,每种只有 1 种方案


🔹 第二步:加入数字 5(完全背包)

cpp 复制代码
    for(int i = 5; i <= n; i++)
        dp[i] += dp[i - 5];
  • 从 5 到 n,顺序遍历
  • 对每个 i,加上"去掉一个 5 后的方案数"
  • 因为是顺序遍历,所以允许多次使用 5(完全背包特性)

💡 举例:

  • i=9: dp[9] += dp[4] = 1 → 表示 4+5
  • i=13: dp[13] += dp[8] = 14×2 + 5
  • i=14: dp[14] += dp[9] = 14 + 5×2 → 总方案数 1 ✅

🔚 输出答案

cpp 复制代码
    cout << dp[n];
    return 0;
}

🧪 样例验证

样例 1:n = 14

  • 4 的倍数:4,8,12 → dp[4]=dp[8]=dp[12]=1
  • 加入 5:
    • dp[9] = dp[4] = 1
    • dp[13] = dp[8] = 1
    • dp[14] = dp[9] = 1
  • 其他组合?5×2 + 4 = 14 → 只有这一种
  • 输出 1

样例 2:n = 40

可能的 (x, y) 满足 4x + 5y = 40

  • y=0 → x=10
  • y=4 → x=5 (5×4=20, 4×5=20)
  • y=8 → x=0
    → 共 3 种

代码计算:

  • dp[40] 会累加:
    • 仅用 4:1 种
    • 用 4 个 5:dp[20](此时 dp[20] 已包含 4×55×4?)

等等,我们验证 dp[20]

  • 仅用 4:dp[20] = 1
  • 加入 5 后:
    • dp[20] += dp[15]
    • dp[15] += dp[10]
    • dp[10] += dp[5]
    • dp[5] += dp[0] = 1
    • 所以 dp[5]=1, dp[10]=1, dp[15]=1, dp[20]=1+1=2
  • 那么 dp[40]
    • 初始(仅4):1
    • dp[40] += dp[35]
    • dp[35] += dp[30]
    • ... 最终 dp[40] = 3

样例 3:n = 6

  • 无法用 4 和 5 拼出 6(4<6<8,5<6<10,且 4+5=9>6)
  • dp[6] 始终为 0 → 输出 0

⚠️ 为什么这个顺序是对的?

  • 先处理 4:确保所有"纯 4"方案被记录
  • 再处理 5:在已有方案(含 4)基础上,添加任意多个 5
  • 由于 5 是从小到大顺序遍历 ,所以:
    • dp[i] 包含了使用 0 个、1 个、2 个......5 的所有方案
  • 而且因为 4 和 5 的处理是分阶段 的,不会出现 (4,5)(5,4) 被算两次(因为顺序不重要,本题只计组合,不计排列)

总结

要点 说明
模型 完全背包计数问题
状态 dp[i] = 拼出 i 的方案数
初始化 dp[0] = 1
转移 先用 4 更新,再用 5 顺序更新
复杂度 O(n),满足 n ≤ 1e6
相关推荐
AI科技星2 小时前
基于空间光速螺旋第一性原理的电荷本源定义与电场时空协变方程的完整推导、严格证明及全尺度数值验证
c语言·开发语言·算法·机器学习·数据挖掘
云泽8082 小时前
蓝桥杯算法精讲:深剖分治算法及其经典应用
算法·职场和发展·蓝桥杯
志摩凛2 小时前
范畴论——前端与计算机领域的“抽象工具箱”:该用则用,该弃则弃
算法·架构
2401_857918292 小时前
C++与自动驾驶系统
开发语言·c++·算法
乐分启航2 小时前
【无标题】
深度学习·算法·目标检测·transformer·迁移学习
liu****2 小时前
linux上git推送内容到gitee上
c++·git·gitee·版本控制
GfovikS061002 小时前
C++中的函数式编程
开发语言·c++·算法
2401_857918292 小时前
C++中的构建器模式
开发语言·c++·算法
liu****2 小时前
6.多人协作(同一分支下(不常用))(一)
c++·gitee·版本控制