洛谷P9234 [蓝桥杯 2023 省 A] 买瓜题解
题目描述
小蓝正在一个瓜摊上买瓜。瓜摊上共有 n n n 个瓜,每个瓜的重量为 A i A_i Ai。小蓝刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只能劈一刀。
小蓝希望买到的瓜的重量的和恰好为 m m m。
请问小蓝至少要劈多少个瓜才能买到重量恰好为 m m m 的瓜。如果无论怎样小蓝都无法得到总重恰好为 m m m 的瓜,请输出 − 1 -1 −1。
输入格式
输入的第一行包含两个整数 n , m n,m n,m,用一个空格分隔,分别表示瓜的个数和小蓝想买到的瓜的总重量。
第二行包含 n n n 个整数 A i A_i Ai,相邻整数之间使用一个空格分隔,分别表示每个瓜的重量。
输出格式
输出一行包含一个整数表示答案。
样例 #1
样例输入 #1
3 10
1 3 13
样例输出 #1
2
提示
【评测用例规模与约定】
对于 20 % 20 \% 20% 的评测用例, n ≤ 10 n \leq 10 n≤10;
对于 60 % 60 \% 60% 的评测用例, n ≤ 20 n \leq 20 n≤20;
对于所有评测用例, 1 ≤ n ≤ 30 1 \leq n \leq 30 1≤n≤30, 1 ≤ A i ≤ 1 0 9 1 \leq A_i \leq 10^9 1≤Ai≤109, 1 ≤ m ≤ 1 0 9 1 \leq m \leq 10^9 1≤m≤109。
对于本题来说,很容易想到dfs爆搜的方法,对于每种瓜来说有三种状态
s t a t { a [ i ] 完整拿取 a [ i ] / 2 半部分拿取 0 不拿 stat \begin{cases} &a[i]&完整拿取\\ &a[i]/2&半部分拿取\\ &0&不拿& \end{cases} stat⎩ ⎨ ⎧a[i]a[i]/20完整拿取半部分拿取不拿
对于爆搜的时间复杂度为 O ( 3 n ) O(3^n) O(3n)跑一遍程序发现 n = 20 n=20 n=20是无法通过的
计算一下发现算法复杂度有问题
面对高指数搜索时有一种降低指数大小的方法——折半搜索(Meet in the middle)
可以将 O ( a n ) O(a^n) O(an)的算法降低为 O ( a n 2 ) O(a^\frac{n}{2}) O(a2n)#include<bits/stdc++.h> using namespace std; #define ll long long int #define mod double ma; ll n; int ck=-1; int ans=1<<30; vector<double>arr; unordered_map<double,int>check;//unordered减少时间,但当数据大小小时可能速度比map慢 void dfs1(int m,double sum,int cnt)//计数器,当前总和,切的次数 { if(n/2==m) { if(check.count(sum))//看一下map里是否有 { check[sum]=min(check[sum],cnt); } else check[sum]=cnt; return; } // none dfs1(m+1,sum,cnt); // all dfs1(m+1,sum+arr[m],cnt); // half dfs1(m+1,sum+arr[m]/2,cnt+1); } void dfs2(int m,double sum,int cnt) { if(m==n-n/2) { if(check.count(ma-sum)) { ck=1; ans=min(ans,cnt+check[ma-sum]); return; } else return; } dfs2(m+1,sum,cnt); // all dfs2(m+1,sum+arr[n/2+m],cnt); // half dfs2(m+1,sum+arr[n/2+m]/2,cnt+1); } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>ma; arr.resize(n); for(int i=0;i<n;i++)cin>>arr[i]; dfs1(0,0,0); dfs2(0,0,0); if(ck==1)cout<<ans; else cout<<-1; return 0; }
交完发现T了
寻找时间更少的方式的几个方向
关闭流同步
剪枝
优化搜索顺序
#include<bits/stdc++.h> using namespace std; #define ll long long int #define mod int ma; int n; int ck=-1; int ans=1<<30; vector<int>arr; unordered_map<int,int>check; //修改点1 double改为了int用乘2消除小数误差,运算速度据网络实测两者相差不大 void dfs1(int m,int sum,int cnt) { if(sum>ma)return;//修改点2 剪枝已经超过目标值的不再继续搜 if(n/2==m) { if(check.count(sum)) { check[sum]=min(check[sum],cnt); } else check[sum]=cnt; return; } // none dfs1(m+1,sum,cnt); // all dfs1(m+1,sum+arr[m],cnt); // half dfs1(m+1,sum+arr[m]/2,cnt+1); } void dfs2(int m,int sum,int cnt) { if(m==n-n/2) { if(check.count(ma-sum)) { ck=1; ans=min(ans,cnt+check[ma-sum]); return; } else return; } dfs2(m+1,sum,cnt); // all dfs2(m+1,sum+arr[n/2+m],cnt); // half dfs2(m+1,sum+arr[n/2+m]/2,cnt+1); } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>ma; ma*=2; arr.resize(n); for(int i=0;i<n;i++){cin>>arr[i];arr[i]*=2;} sort(arr.begin(),arr.end()); //修改点3 优化搜索顺序,先搜大的值提前筛掉超过的值 dfs1(0,0,0); dfs2(0,0,0); if(ck==1)cout<<ans; else cout<<-1; return 0; }
操作完AC