《DFS》《剪枝》Problem C. 买蛋糕

探讨如何使用最少数量的不同正整数来组合成任意小于等于n的正整数,通过递归搜索和剪枝策略寻找最优解。

Problem C. 买蛋糕

时间限制 1000 ms
内存限制 128 MB

题目描述

  野猫过生日,大家当然会送礼物了(咳咳,没送礼物的同志注意了哈!!),由于不知道送什么好,又考虑到实用性等其他问题,大家决定合伙给野猫买一个生日蛋糕。大家不知道最后要买的蛋糕的准确价格,而只会给蛋糕估价,即要买一个不超过多少钱的蛋糕。众OIer借此发挥:能否用最少的钱币数去凑成估价范围内的所有价值,使得不管蛋糕价值多少,都不用找钱……
  现在问题由此引出:对于一个给定的n,能否用最少的不等的正整数去组成n以内(包括n)的所有的正整数呢?如果能,最少需要多少个正整数,用最少个数又有多少不同的组成方法呢?

输入数据

只有一行包含一个整数 n (1≤n≤1000)n (1≤n≤1000) 。

输出数据

一行两个数,第一个数是最少需要多少个数,第二个数是用最少个数的组成方案个数。两个答案用空格分隔。

样例输入

6

样例输出

3 2

样例说明

最少用三个数,有两种方法,分别是:1,2,3和1,2,4。
对于1,2,3有1,2,3,1+3,2+3,1+2+3;
对于1,2,4有1,2,1+2,4,1+4,2+4。

整体思路:

剪枝:基元数为m,当确定m-1个基元之后要确定第m个基元时,
首先 第m个基元>maxI且第m个基元<=n+1  当第m个基元>n+1 会出现n+1这个数无法表示  如 1 2 5(5=3+2)  4无法被表示
其次会出现3种情况
1.2*sum+1<n(sum:m-1个基元确定的最大值n(m-1));return 0 因为m基元最大是sum+1
2.sum+maxI+1>=n 会增加 sum+1-(maxI+1)+1=sum-maxI+1种情况;
3. else sum+x>=n  (n>maxI+1) x=n-sum    会增加 sum+1-(n-sum)+1=2*sum-n+2种情况;

最后呢,对所有可能的基元进行搜索即可。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#define INF 0x7FFFFFFF //int 最大值

using namespace std;

int n, m;// n:要表示1...n的数  m:最小基元数
int result = 0;
//基元为m,当确定m-1个基元时,要确定第m个基元时,
// 首先 m基元>maxI且m基元<=n+1  当m基元>n+1 会出现n+1这个数无法表示  如 1 2 5(5=3+2)  4无法被表示
//其次会出现3种情况
// 2*sum+1<n(sum:m-1个基元确定的最大值n(m-1));return 0 因为m基元最大是sum+1
//sum+maxI+1>=n 会增加 sum+1-(maxI+1)+1=sum-maxI+1中情况;
// else sum+x>=n  (n>maxI+1) x=n-sum    会增加 sum+1-(n-sum)+1=2*sum-n+2种情况;

int dfs(int num,int maxI,int sum)//num 基元的个数 maxI 最大基元, sum 基元组成的最大和 即所有基元相加
{
	if (num == m)// 要确定m基元了
	{
		if (2 * sum + 1 < n)//不能满足条件,不加情况。
			return 0;
		else if (sum + maxI + 1 >= n)
			result += sum - maxI + 1;
		else
			result += 2 * sum + 2 - n;
        return 1;
	}
    for (int i = sum + 1; i >= maxI + 1; i--)//m基元>maxI且m基元<=n+1 对于maxI+1到sum+1一次遍历
        dfs(num + 1, i,sum+i);
}
int main()
{
	cin >> n;
	//因为电脑是二进制的,所以p个数字最多能组成2^p-1  eg 1 2 4 2^3-1=7  就是1+2+4=7; 反之1...n最少需要向下取整log(n)+1个
	m = floor(log2(n)) + 1;//m就是需要使用的最少个数
	dfs(1, 0, 0);//从第一个数开始确定
	cout << m << " " << result << endl;
	return 0;
}

 

### 关于DFS剪枝技术 #### 剪枝技术概述 深度优先搜索(DFS)作为一种强大的工具,在面对庞大搜索空间时可能会陷入过多无效探索之中。为此,“剪枝”作为一项关键技术被引入,其目的在于通过设定特定条件提前终止那些不可能导向解决方案的分支,以此达到缩小搜索范围并加快求解速度的效果[^1]。 #### 实现方式 在实际操作层面,实现有效的剪枝依赖于合理设置过滤条件——即所谓的“剪枝原则”。这涉及到识别哪些部分的搜索路径是可以安全忽略掉而不影响最终结果获取的能力。例如,在处理二叉树结构时,可以通过比较节点值与目标值之间的关系决定是否继续深入该子树进行查找;对于数值型问题,则可以利用上下界约束来排除明显不符合要求的情况[^2]。 ```cpp class Solution { public: int kthSmallest(TreeNode* root, int k) { count = k; DFS(root); return ret; } private: int count, ret; void DFS(TreeNode* root) { if (root == nullptr || count <= 0) return; // 当遇到空结点或已经找到第k小元素时停止递归 DFS(root->left); // 首先访问左子树 if (--count == 0) { // 如果当前是第k个最小值 ret = root->val; // 记录下这个值 return; // 并立即返回不再向下执行 } DFS(root->right); // 接着访问右子树 } }; ``` 上述代码展示了如何在一个简单的场景中运用剪枝原理优化DFS算法。这里的关键在于当`count`减至零时立刻结束进一步的搜索动作,避免浪费资源遍历剩余未检查的部分[^4]。 #### 设计思路 从更广泛的角度来看,设计良好的剪枝机制不仅限于单纯的技术手段选择,更重要的是要基于对具体应用场景深刻理解的基础上构建合适的逻辑框架。这意味着开发者应当充分考虑待解决问题的特点以及数据分布规律等因素,进而制定出既高效又可靠的裁剪标准[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值