题目链接:https://www.luogu.org/problemnew/show/P2563
题目描述
任何大于 1 的自然数 n 都可以写成若干个大于等于 2 且小于等于 n 的质数之和表达式(包括只有一个数构成的和表达式的情况),并且可能有不止一种质数和的形式。例如,9 的质数和表达式就有四种本质不同的形式:
9 = 2 + 5 + 2 = 2 + 3 + 2 + 2 = 3 + 3 + 3 = 2 + 7 。
这里所谓两个本质相同的表达式是指可以通过交换其中一个表达式中参加和运算的各个数的位置而直接得到另一个表达式。
试编程求解自然数 n 可以写成多少种本质不同的质数和表达式。
输入输出格式
输入格式:
文件中的每一行存放一个自然数 n(2 < n < 200) 。
输出格式:
依次输出每一个自然数 n 的本质不同的质数和表达式的数目。
输入样例#1:
2
200
输出样例#1:
1
9845164
稍微分析
状态转移方程: f[j] = f[j] + f[j-prime[i]]
动态规划的题思路每次都不好讲清楚,感觉最重要的还是结合代码和状态转移方程来领悟啊
AC代码
#include <bits/stdc++.h>
using namespace std;
int f[201];//记录当前下标n的 本质不同的质数和,初始值全为0
int pri[201];//用来排除非质数,初始值全为0
void add(int x)
{
//当前不同的质数和f[i]由自身f[i]和i减去一个质数x(f[i-x]) 的 不同的质数和组成
for(int i=x; i<=200; i++)
f[i]+=f[i-x];
}
void init()
{
f[0]=1;//很容易忘记,很重要!(本身的意义是:质数不拆封也算一种质数和)
for(int i=2; i<=200; i++) {
if(!pri[i]) { //当前i为质数进入if语句
add(i);
for(int j=i*i; j<=200; j+=i) //巧妙去除非质数
pri[j]=1;
}
}
}
int main()
{
init();
int n;
while(cin>>n) {//这里实际不推荐这么写,毕竟也因此被T了好多次了
cout<<f[n]<<endl;
}
return 0;
}
代码2
和上面代码的主要区别在于:排除非质数的方法不同
#include <bits/stdc++.h>
using namespace std;
int f[201];//记录当前下标n的 本质不同的质数和,初始值全为0
bool if_prime(int n)
{
//if(n<=1) return false;
for(int i=2; i*i<=n; i++) {
if(n%i==0) return false;
}
return true;
}
void init()
{
f[0]=1;//本身的意义是:质数不拆封也算一种质数和
for(int i=2; i<=200; i++) {
if(if_prime(i) ) { //当前i为质数进入if语句
int x=i;
for(int j=x; j<=200; j++)
f[j]+=f[j-x];
}
}
}
int main()
{
init();
int n;
while(cin>>n)
cout<<f[n]<<endl;
return 0;
}