深度优先搜索 幸运的袋子

 

分析:

1. 这是一个组合问题,求可行方案的数目,因此无法用动态规划。除了暴力搜索,必须进行剪枝!

    有几个问题必须想清楚:

    (1)为什么要排序?而且还是从小到大排序?从大到小排序不行吗?

       回答:我们考虑是否要加入一个新元素 y,假设前面已经选中了若干个数,这些数的和为 s, 乘积为 p。一般来说, s > 1 (因为都是正整数,s 必定>=1, 等于1的情况只有一种,就是前面只选了一个数,而且这个数是1),若要为幸运袋,令 s + y > p * y,得到 y < s / (p-1),可知:如果我们按照升序排列,当第 i 个数 a[i] 不满足题意,即 y >= s / (p-1)时,后面的元素必定不满足题意,因此可以得到本题最为重要的第一个剪枝:如果搜索到 a[i] 不满足题意,后面直接 pass,即 break

    (2)题目中说,相同号码的球不作区分,如何避免重复计算?

       回答:当搜索到 a[i] 时,如果后面的若干个球的号码与 a[i] 相同,则直接 pass !

/*
一个袋子里面有n个球,每个球上面都有一个号码(拥有相同号码的球是无区别的)。如果一个袋子是幸运的当且仅当所有球的号码的和大于所有球的号码的积。
例如:如果袋子里面的球的号码是{1, 1, 2, 3},这个袋子就是幸运的,因为1 + 1 + 2 + 3 > 1 * 1 * 2 * 3
你可以适当从袋子里移除一些球(可以移除0个,但是别移除完),要使移除后的袋子是幸运的。现在让你编程计算一下你可以获得的多少种不同的幸运的袋子。
分析:这个问题不是最优化问题,也不存在子问题重叠,因此无法用动态规划和贪心
      因此,可以用搜索,为了快速编程,推荐用dfs,此时一定要好好考虑如何剪枝
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
#include<string>
#include<set>
#include<queue>
using namespace std;

int const maxn = 1002;
int n,a[maxn];
int ans = 0;	// 满足条件的方法数目

int dfs(int a[], int pos, long long sum, long long product){
	// Step 1. 判断终止条件
	// Step 2. 继续遍历当前数组
	int cur_ans = 0;		    // 当前符合题意的方案数目
	for (int i = pos; i < n; i++){
		// 判断当前元素是否可用
		sum += a[i];
		product *= a[i];
		if (sum > product)		// 满足题意
			cur_ans += 1 + dfs(a, i + 1, sum, product);
		else if (a[i] == 1)		// 此问题的特殊性,必须单独考虑 a[i]==1 的情况
			cur_ans += dfs(a, i + 1, sum, product);
		else                    // 当前元素不满足题意,那么后面的数也不必考虑,直接剪枝!
			break;
		
		// Step 3. 回溯!
		sum -= a[i];
		product /= a[i];
		// 剪枝2: 跳过相同值的连续元素,原因是题目中说了,相同号码的球被认为是无区别的
		for (; i<n - 1 && a[i] == a[i + 1]; ++i);
	}
	return cur_ans;
}

int main(int argc, char * argv[])
{
	while (cin >> n){
		for (int i = 0; i < n; i++)
			scanf("%d", &a[i]);

		// Step one: sort in ascending order
		sort(a, a + n);

		cout << dfs(a, 0, 0, 1) << endl;
	}
	return 0;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值