[蓝桥杯][算法提高] ADV-205 拿糖果【动态规划】题解dp分析与C++示例代码

试题 算法提高 拿糖果

资源限制
时间限制: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(iPjPj)+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<PjM

集合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(i2×Pj)+Pj)

( 1 ≤ i ≤ N 1≤i≤N 1iN 1 < P j ≤ M 1<P_j≤ \sqrt{M} 1<PjM 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;
}
糖果是一道经典的编程题目,常出现在编程竞赛中,比如蓝桥杯。题目描述如下: 给定一个长度为n的数组,表示n个小朋友的评分。现在需要将糖果分给这些小朋友,要求满足以下条件: 1. 每个小朋友至少分到一个糖果。 2. 评分更高的小朋友比他相邻的小朋友分到的糖果数量更多。 要求:计算最少需要多少个糖果才能满足以上条件。 解题思路: 1. 首先初始化一个长度为n的糖果数组,每个元素都为1,表示每个小朋友至少分到一个糖果。 2. 从左到右遍历评分数组,如果当前小朋友的评分比前一个小朋友高,则将当前小朋友的糖果数量设置为前一个小朋友的糖果数量加1。 3. 从右到左再遍历一次评分数组,如果当前小朋友的评分比后一个小朋友高,并且当前小朋友的糖果数量不大于后一个小朋友的糖果数量,则将当前小朋友的糖果数量设置为后一个小朋友的糖果数量加1。 4. 最后将糖果数组中所有元素的值相加,即为最少需要的糖果数量。 下面是分糖果问题的Python代码示例: ```python def minCandies(ratings): n = len(ratings) candies = * n for i in range(1, n): if ratings[i] > ratings[i-1]: candies[i] = candies[i-1] + 1 for i in range(n-2, -1, -1): if ratings[i] > ratings[i+1] and candies[i] <= candies[i+1]: candies[i] = candies[i+1] + 1 return sum(candies) # 测试样例 ratings = [1, 3, 2, 1, 2, 4, 3] result = minCandies(ratings) print(result) # 输出:12 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值