试题 算法提高 拿糖果
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
妈妈给小B买了N块糖!但是她不允许小B直接吃掉。
假设当前有M块糖,小B每次可以拿P块糖,其中P是M的一个不大于根号下M的质因数。这时,妈妈就会在小B拿了P块糖以后再从糖堆里拿走P块糖。然后小B就可以接着拿糖。
现在小B希望知道最多可以拿多少糖。
输入格式
一个整数N
输出格式
最多可以拿多少糖
样例输入
15
样例输出
6
数据规模和约定
N <= 100000
题解:
每个物品,要么选,要么不选,选法数量最多 2 n 2^n 2n
科普,质数和质因数:
一开始以为质因数就是质数,只拿到了一点分o(︶︿︶)o ,参考了他人的代码才反应过来
DP分析(选择问题,背包模型):
1.状态表示(化0为整):
(1)集合:
- f ( i ) f(i) f(i):表示当总糖果数为 i i i时,小B最大可以拿到的糖果数的集合
(2)属性:(值和集合的关系)
- m a x max max(看问题:集合中每个方案总价值最大):
- f ( N ) f(N) f(N) (当糖果数为N)
2.状态计算(集合划分:化整为0的过程):
-
集合划分:
-
集合1:当总糖果数为 i i i时,小B不拿第 i i i个糖果可以拿到的最大糖果数仍是: f ( i ) f(i) f(i)
-
集合2:当总糖果数为 i i i时,小B拿第 i i i个糖果的集合: f ( i − P j − P j ) + P j f(i-P_j-P_j)+P_j f(i−Pj−Pj)+Pj
- 解释:( 当糖果数为
i
i
i时小B能拿到的糖果数
=
=
= 糖果数为
i
i
i个时的上一个状态
+
P
i
+P_i
+Pi )(变化+不变)
- 是否拿
P
j
P_j
Pj个糖果需要满足条件:
- 1️⃣ P j P_j Pj是 i i i的质因数(即是素数且能被 i i i整除才是 i i i的质因数)即i % Pj[j] == 0
- 2️⃣ 1 < P j ≤ M 1<P_j≤ \sqrt{M} 1<Pj≤M
- 是否拿
P
j
P_j
Pj个糖果需要满足条件:
- 解释:( 当糖果数为
i
i
i时小B能拿到的糖果数
=
=
= 糖果数为
i
i
i个时的上一个状态
+
P
i
+P_i
+Pi )(变化+不变)
-
集合1,2取最大值,则可以推出拿糖果的dp的方程:
f
(
i
)
=
m
a
x
(
f
(
i
)
,
f
(
i
−
2
×
P
j
)
+
P
j
)
f(i)=max( f(i) , f(i-2×P_j)+P_j)
f(i)=max(f(i),f(i−2×Pj)+Pj)
( 1 ≤ i ≤ N 1≤i≤N 1≤i≤N, 1 < P j ≤ M 1<P_j≤ \sqrt{M} 1<Pj≤M且 P j P_j Pj为 i i i的质因数(能被 i i i整除且是素数)
C++实例代码:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int f[100002], Pj[100002];
int cnt = 0;//记录1到 √M的素数个数
int isPrime(int num) {//判断是否素数,是则return 1
if (num <= 1)return false;
if (num == 2)return true;
if (num % 2 == 0)return false;
for (int i = 3; i*i <= num; i += 2) {
if (num%i == 0) {
return false;
}
}
return true;
}
void MPrime(int M) {//创建小于根号M的素数表
int len = sqrt(M);
for (int i = 2; i <= len; ++i) {
if (isPrime(i)) {//是素数
Pj[cnt] = i; //记录素数表
cnt++;
}
}
}
int main() {
int M;
cin >> M;
MPrime(M);
for (int i = 1; i <= M; i++) {//i为糖果数
for (int j = 0; j < cnt; j++) {//j遍历M的素数表
if (Pj[j] <= sqrt(i) && i % Pj[j] == 0) {
//当前素数 比 根号i 大 ,跳过,拿不了那么多糖果 //是素数且能被i整除才是i的质因数
f[i] = max(f[i], f[i - 2 * Pj[j]] + Pj[j]);
}
}
}
cout << f[M];
return 0;
}