题目描述
小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜,每个瓜的重量为 Ai 。
小蓝刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只能劈一刀。
小蓝希望买到的瓜的重量的和恰好为 m 。
请问小蓝至少要劈多少个瓜才能买到重量恰好为 m 的瓜。如果无论怎样小蓝都无法得到总重恰好为 m 的瓜,请输出 −1 。
输入格式
输入的第一行包含两个整数 n, m,用一个空格分隔,分别表示瓜的个数和小蓝想买到的瓜的总重量。
第二行包含 n 个整数 Ai,相邻整数之间使用一个空格分隔,分别表示每个瓜的重量。
输出格式
输出一行包含一个整数表示答案。
样例输入
3 10
1 3 13
样例输出
2
提示
对于 20% 的评测用例,∑n≤10;
对于 60% 的评测用例,∑n≤20;
对于所有评测用例,1 ≤n≤30,1≤ Ai ≤ 10^9 ,1 ≤ m ≤ 10^9
整体思路
对于每一个瓜有三种选择:
1)买整个瓜
2)买半个瓜,需要增加劈瓜次数
3)不买
考虑深度优先搜索,对每个瓜的三种选择进行搜索,解空间树是一颗完全三叉树,时间复杂度为O(3^n),故需要进行剪枝。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 35;
int v[N]; //重量数组
long suf[N]; // 重量数组的后缀和,即当前瓜重量加上后面所有瓜重量
int n, m;
int ans = INT_MAX;
// 当前层数 pos,当前总重量 sum,当前劈瓜的次数 cnt
void dfs(int pos, long sum, int cnt)
{
if(sum == m)
{
ans = min(ans, cnt);
return;
}
// 剪枝
if(pos >= n || cnt >= ans || sum > m || sum + suf[pos] < m)
{
return;
}
dfs(pos + 1, sum + v[pos], cnt); // 买整个瓜
dfs(pos + 1, sum + v[pos] / 2, cnt + 1); // 买半个瓜
dfs(pos + 1, sum, cnt); // 不买
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
cin >> n >> m;
m *= 2;
for(int i = 0; i < n; i++)
{
cin >> v[i];
v[i] *= 2;
}
sort(v, v + n, greater<int>());
for(int i = n - 1; i >= 0; i--)
{
suf[i] = suf[i + 1] + v[i];
}
dfs(0, 0, 0);
if(ans == INT_MAX)
{
cout << -1;
exit(0);
}
cout << ans;
return 0;
}
具体步骤
买半个瓜时需要将重量除 2,会产生小数,故可以将重量数组都乘 2,最大重量也乘 2。为了防止超时,将重量数组降序排列,使得更快剪枝。并创建一个重量数组的后缀数组 suf,这样在搜索时可以利用其剪枝。