P1832 A+B Problem(再升级)

记录110

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
long long dp[1010];//注意longlong 
bool f(int x){//判断素数 
	if(x<2) return false;
	for(int i=2;i*i<=x;i++){
		if(x%i==0) return false;
	}
	return true;
} 
int main(){//完全背包 
	int n; 
	cin>>n;
	dp[0]=1;//dp起点 
	for(int i=2;i<=n;i++){//遍历数字 
		if(f(i)){//判断素数i 
			for(int j=i;j<=n;j++) dp[j]+=dp[j-i];//用素数i来组成数字j 
		}
	}
	cout<<dp[n];//输出 
	return 0;//结束程序 
}

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


突破口

给定一个正整数 n,求将其分解成若干个素数之和的方案总数。


🔍 一、题目核心理解

🎯 问题描述

给定一个正整数 n,求 将 n 表示为若干个素数之和的方案总数

✅ 注意:

  • 顺序不同视为同一种方案 (例如 2+55+2 算一种)
  • 允许重复使用同一个素数 (如 2+2+3 是合法的)
  • 每个加数必须是 素数

这实际上是一个 "用素数作为物品,组成总和为 n 的方案数" 的问题。


📌 样例解析

输入 #1:n = 7

合法分解(不考虑顺序):

  1. 7
  2. 2 + 5
  3. 2 + 2 + 3

共 3 种 → 输出 3

输入 #2:n = 20 → 输出 26(无需手动验证)


🧠 二、解题思路:完全背包计数模型

关键观察

  • 素数可以重复使用(如两个 2)
  • 方案不考虑顺序(即组合而非排列)
  • 这正是 完全背包的"组合类计数"问题

💡 对比:

  • 如果考虑顺序(如 2+55+2 不同),则是"排列型",需外层遍历容量
  • 本题要求无序组合 → 应外层遍历物品(素数),内层遍历容量

动态规划设计

  • dp[j]:表示组成数字 j 的方案总数
  • 初始化:dp[0] = 1(空和,1 种方案)
  • 对每个素数 p(从小到大枚举):
    • j = p to n
      • dp[j] += dp[j - p]

✅ 这样能保证:每种组合只按素数递增顺序生成一次,避免重复计数


分析代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
long long dp[1010]; // dp[j]:组成 j 的方案数,注意可能很大,用 long long
  • n ≤ 1000,但方案数可能指数级增长(如 n=1000 时方案数极大)
  • 所以用 long long 防止溢出(题目未说取模,需存大数)
cpp 复制代码
bool f(int x){ // 判断 x 是否为素数
    if(x < 2) return false;
    for(int i = 2; i * i <= x; i++){
        if(x % i == 0) return false;
    }
    return true;
}
  • 标准的素数判断函数
  • x < 2 → 非素数
  • 试除到 √x 即可
cpp 复制代码
int main(){
    int n; 
    cin >> n;
    dp[0] = 1; // 基础情况:和为 0 有 1 种方案(什么都不选)
cpp 复制代码
    for(int i = 2; i <= n; i++){ // 枚举所有可能的"物品":2 到 n
        if(f(i)){ // 如果 i 是素数
            for(int j = i; j <= n; j++) 
                dp[j] += dp[j - i]; // 完全背包:用素数 i 更新 dp[j]
        }
    }

🔄 核心逻辑说明:

  • 外层循环 :遍历所有可能的素数 i(从 2 到 n)
  • 内层循环j = i to n正序!
    • 正序是完全背包的标志(允许重复使用)
    • dp[j] += dp[j - i]:表示在已有方案基础上,加入一个 i

✅ 为什么这样不会重复计数?

  • 因为我们按素数从小到大依次加入
  • 所有方案都以"非递减素数序列"形式生成
  • 例如:2+2+3 会被生成,但 3+2+2 不会(因为 3 在 2 之后才被考虑)
cpp 复制代码
    cout << dp[n]; // 输出组成 n 的方案总数
    return 0;
}

⚠️ 关键细节说明

细节 说明
dp[0] = 1 组合计数问题的标准起点
素数判断范围 只需判断 2..n,因为大于 n 的素数不可能用于组成 n
内层正序 完全背包特征,允许多次使用同一素数
外层素数顺序 保证组合无序,避免 2+55+2 被重复计算
数据类型 long long 防止方案数溢出(n=1000 时方案数可达数百万甚至更多)

📊 补充:为什么这是"完全背包"?

背包类型 物品使用次数 内层循环方向
0-1 背包 每件最多1次 倒序
完全背包 每件无限次 正序

本题中,每个素数可用任意多次 (如多个 2),所以是完全背包


总结:问题与解法对应

题目要求 DP 设计
分解为素数之和 物品 = 所有 ≤n 的素数
允许重复 完全背包
不考虑顺序 外层遍历素数(从小到大)
求方案总数 dp[j] += dp[j - p],初始化 dp[0]=1
相关推荐
sali-tec2 分钟前
C# 基于OpenCv的视觉工作流-章108-区域筛选
图像处理·人工智能·opencv·算法·计算机视觉
Gigavision6 小时前
基于BUAA-MIHR数据集的噪声解耦对比学习算法
人工智能·python·深度学习·算法
鹿角片ljp6 小时前
LeetCode 64:最小路径和复盘|二维 DP 与 ACM 模式完整写法
java·数据结构·算法
彧azz7 小时前
算法设计与分析:贪心与动态规划
数据结构·学习·算法·贪心算法·动态规划
Beyond_System|系统之外8 小时前
【学编程】Python基础编程题100道(21-60)
开发语言·python·算法
景熙55238 小时前
15.Java 8 Stream 流入门到实战
java·开发语言·数据结构
汉克老师9 小时前
GESP2026年9月认证C++六级( 第三部分编程题(1、数组划分))精讲
c++·gesp·小学生·学c++编程
千里码aicood9 小时前
基于PLC与机器视觉的智能分拣控制系统设计
c++·自动化·plc
漂流瓶jz10 小时前
UVA-1442 洞穴 题解答案代码 算法竞赛入门经典第二版
c++·算法·题解·aoapc·算法竞赛入门经典·uva
朽棘不雕10 小时前
进一步了解模板
c++