问题描述
小蓝正在一个瓜摊上买瓜。瓜摊上共有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.
解题思路
本题我第一个想到的是dfs,维护一个最小的劈瓜次数ans,用一个瓜数组记录每个瓜的重量,遍历每一个瓜,可以分成三种情况,选一个完整的瓜,不选该瓜,切一刀选半个瓜,后续分别对应瓜状态数组f[i]=1,2,3,0 (0为该瓜还未考虑到),dfs函数传递3个参数,分别是瓜的编号i,已经挑选的瓜总量sum,切的次数cnt。判断边界即为挑选的瓜数sum达到需要的数量m,或者遍历完所有瓜。大概思路是这样,但本题需要剪枝,如果不剪枝只能得一部分分数。剪枝有两次,一是当后续方案中劈瓜次数cnt>=ans时可直接舍弃该方案,直接返回。二是当中途挑选的瓜重量sum+b[i]<m时(此处b[i]为后缀和,即当前重量加上后面所有瓜重量都小于所需重量),舍弃该方案,直接返回。值得注意的是做完剪枝后还是不能得满分,后续看了大佬的题解,此题还需要排序,将重量大的瓜放在前面优先考虑,可以减少遍历时间。
完整代码
#include <iostream>
#include <algorithm>
using namespace std;
int ans=35,n,m,q[50],f[50];//f[i]为0为暂未考虑,为1为选,为2为不选,为3为切一刀选一半
long long a[50],b[50],total=0;
void dfs(int i,double sum,int cnt){//瓜编号,已得瓜重量,切的瓜数
if(cnt>=ans) return;//剪枝
if(sum==m){
if(cnt<=ans) ans=cnt;//满足条件,维护最小ans
}
if(sum>=m || i>n || sum+b[i]<m) return;//剪枝
//1
f[i]=1;
dfs(i+1,sum+q[i],cnt);
f[i]=0;
//2
f[i]=2;
dfs(i+1,sum,cnt);
f[i]=0;
//3
f[i]=3;
dfs(i+1,sum+q[i]/2.0,cnt+1);
f[i]=0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>q[i];
total+=q[i];
}
sort(q+1,q+n+1,greater<int>());//反向排序,从大到小
for(int i=n;i>=1;i--){
b[i]=q[i]+b[i+1];//后缀和,即从i到n的和
}
if(total<m){
cout<<-1;
}//瓜总量小于m直接输出-1
else{
dfs(1,0,0);
if(ans==35) cout<<-1;//ans初始值没有改变,说明没有任何一种方案满足条件
else cout<<ans;
}
return 0;
}