题解:P1832 A+B Problem(再升级)
别的大佬们都用完全背包做的,蒟蒻(我)提供一种添加限制的方法。
Analysis
开始会想到枚举小于
设
发现
在做计数类题目的时候,有一种常用的方法,即给函数添加一个限制条件,以保证枚举全的情况下避开重复的情况。
不妨设
那么对
这样五种策略都凑齐了,再验证几个数字,发现都是正确的。
这样为什么是对的?
答:因为我们加了小于等于
Code
代码实现:先筛素数,然后用记忆化搜索进行动态规划。递归的终止条件是
这样的时间复杂度约为
十年 OI 一场空,不开 long long 见祖宗。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
int prime[maxn], cnt; // 存储素数的数组、数组长度的指针
int vis[maxn]; // 筛素数的数组
void getPrime()
{
for(int i = 2; i < maxn; i ++){
if(!vis[i]){
prime[cnt ++] = i;
for(int j = 2 * i; j < maxn; j += i) vis[j] = 1;
}
}
}
ll f[1010][1010];
ll dfs(int m, int l) // 以小于等于l的素数拆分m的方案数
{
if(m == 0) return 1; // 如果减到0了,证明可以成功分解
if(f[m][l] != -1) return f[m][l];
f[m][l] = 0; // 初始化为0,不然会累加上-1
for(int i = 0; prime[i] <= l && prime[i] <= m; i ++){ // 枚举小于l和m的,不然会把m减出负数
f[m][l] += dfs(m - prime[i], prime[i]);
}
return f[m][l];
}
signed main()
{
getPrime();
memset(f, -1, sizeof f);
int n;
cin >> n;
cout << dfs(n, n) << endl;
return 0;
}