幸运的袋子——它使我不幸——由于在这里卡了太长的时间,我的计划全泡汤了。
题目描述:
链接:幸运的袋子__牛客网
来源:牛客网
一个袋子里面有n个球,每个球上面都有一个号码(拥有相同号码的球是无区别的)。如果一个袋子是幸运的当且仅当所有球的号码的和大于所有球的号码的积。
例如:如果袋子里面的球的号码是{1, 1, 2, 3},这个袋子就是幸运的,因为1 + 1 + 2 + 3 > 1 * 1 * 2 * 3
你可以适当从袋子里移除一些球(可以移除0个,但是别移除完),要使移除后的袋子是幸运的。现在让你编程计算一下你可以获得的多少种不同的幸运的袋子。
输入描述:
第一行输入一个正整数n(n ≤ 1000)
第二行为n个数正整数xi(xi ≤ 1000)
输出描述:
输出可以产生的幸运袋子数
示例
输入
3
1 1 1
输出
2
即【1 1 1】与【1 1】。
分析:
这道题最开始是想使用动态规划的,但后来发现并不需要,依照题目中的要求并不会多次重复地碰到某些状态,并且,那些多次重复出现地状态反倒是我们应该使用剪枝剪掉的。
回溯算法是我对于这类题所可以想出的最优解法。
值得注意的是:
所圈出的部分虽然在我们看来用的不是一个 1 ,但是题目中有着这样的描述
并且,我们很容易可以知道,在这样的一棵树中,同一层,拥有相同的值的结点,位置在前者包含了在后者的所有延伸路径——简单来说就是,在某一层中,如果某个数字已经出现过了,那么这一层中其它的与它值相同的枝干都可以被剪掉。
所以,最终的结果应该为:
即一下六种:
1 1
1 1 2
1 1 2 3
1 1 3
1 2
1 3
代码思路:
使用深度优先搜索dfs来对我们创建的树进行遍历。
为了方便计数,创建一个全局变量cnt来对所有的可能进行计数。
对待判断的数组进行排序,方便我们对于相同值枝干的剪枝。
开始深度优先搜索。
当 乘积mul 与 总和sum 的关系满足 sum > mul 时,对全局变量cnt进行自家操作,递归进入树的下一层。
在不满足上一个条件的情况下,判断元素是否为 1 ,如果是,进入下一层循环,如果不是,直接跳出当前循环。这个判断只会在传入的元素是第一个元素且值为 1 的情况下进入。
在 xi 为正整数的前提下,数组的最小元素不可能为 0 。
且,在传入两个 1 的情况下, 1 + 1 > 1 * 1 ,并不会触发这个判断。
同时为了剪枝重复项,在循环体的最后,使用一个 while 循环确保跳过所有的“后续重复项”。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int cnt = 0;
void dfs(vector<int>& arr, int n, int m, int mul, int sum) {
for (int i = m; i < n; ++i) {
mul *= arr[i];
sum += arr[i];
if (sum > mul) {
++cnt;
dfs(arr, n, i + 1, mul, sum);
} else if (arr[i] == 1) {
dfs(arr, n, i + 1, mul, sum);
} else {
break;
}
mul /= arr[i];
sum -= arr[i];
while (i < n - 1 && arr[i] == arr[i + 1]) {
++i;
}
}
}
int main() {
int n;
cin >> n;
vector<int> arr(n);
for (int& num : arr) {
cin >> num;
}
sort(arr.begin(), arr.end());
dfs(arr, n, 0, 1, 0);
cout << cnt << endl;
return 0;
}