Uva 1213 - Sum of Different Primes(DP)
题目链接 https://vjudge.net/problem/UVA-1213
【题意】
给定两个正整数n, k (n<=1120,k<=14), 求用k个互不相同的素数凑出和为n的方案总数
【思路】
先要用埃氏筛选打出[0,1120]内的素数表,下一步就是动态规划了,这个问题类似于01背包问题,设dp(k,i,j)表示在前面k个素数中选出j个素数使他们的和为i的方案数。状态转移方程也类似于01背包,如下所示:
dp(k+1,i,j)=dp(k,i,j) (其它情况)
=dp(k,i-prime[k],j-1) (i>=prime[k]&&j-1>=0)
然后按照这个递推式求解即可,注意边界条件, 当k=0时,dp[0][0][0]=1, dp[0][i][j]=0,当i=0,j=0时dp[k][0][0]=0. 同时还要注意的是循环的内外层关系,通过观察上面的递推公式可知应当现将k从小到大枚举,然后j从小到达枚举,最后是i
最后,这个dp数组可以和01背包问题一样进行状态压缩,将第一维的k去掉,因为我们最后只关心从1120以内所有的素数中去取素数而不是从前面的若干项中去取素数,可以节约内存空间。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1500;
int num;
bool isprime[maxn];
vector<int> prime;
void init() {
fill(isprime, isprime + maxn, 1);
isprime[0] = isprime[1] = 0;
for (int i = 2; i < maxn; ++i) {
if (isprime[i]) {
prime.push_back(i);
for (int j = i + i; j < maxn; j += i) isprime[j] = 0;
}
}
num = prime.size();
}
int dp[maxn][maxn][15];
void solve() {
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
for (int k = 0; k < num; ++k) {
for (int j = 0; j < 15; ++j) {
for (int i = 0; i < maxn; ++i) {
if (j == 0 && i == 0) {
dp[k + 1][i][j] = 1;
continue;
}
dp[k + 1][i][j] = dp[k][i][j];
if (i >= prime[k] && j - 1 >= 0) dp[k + 1][i][j] += dp[k][i - prime[k]][j - 1];
}
}
}
}
int main() {
int n, k;
init();
solve();
while (scanf("%d%d", &n, &k) && (n + k)) {
int ans = dp[num][n][k];
printf("%d\n", ans);
}
return 0;
}
优化代码#include<bits/stdc++.h>
using namespace std;
const int maxn = 1500;
int num;
vector<int> p;//素数表
bool isp[maxn];
int dp[maxn][15];
void init() {//筛选素数
fill(isp, isp + maxn, 1);
isp[0] = isp[1] = 0;
for (int i = 2; i < maxn; ++i) {
if (isp[i]) {
p.push_back(i);
for (int j = i * 2; j < maxn; j += i) isp[j] = 0;
}
}
num = p.size();
}
void solve() {//dp求解
dp[0][0] = 1;
for (int k = 0; k < num; ++k) {
for (int j = 14; j >= 1; --j) {
for (int i = maxn - 1; i - p[k] >= 0; --i) {
/*
注意这两个内层循环一定要倒着写,比如针对j而言,推算dp[i][j]时要用dp[i-p[k]][j-1]也就是j-1的结果,
如果不这样写,循环从小往大,那么我们就会先更新dp[i-p[k]][j-1],等到更新dp[i][j]时,dp[i-p[k]][j-1]
已经不是我们需要的值了,对i也是同样的道理,这些都是通过观察递推方程得出的
*/
dp[i][j] += dp[i - p[k]][j - 1];
}
}
}
}
int main() {
init();
solve();
int n, k;
while (scanf("%d%d", &n, &k) == 2 && (n + k)) {
int ans = dp[n][k];
printf("%d\n", ans);
}
return 0;
}