P9234 [蓝桥杯 2023 省 A] 买瓜 题解
upd on 2025/7/20:攻克了 hack 数据,申请再次通过。
已在文末给出最新代码。
https://www.luogu.com.cn/record/225727835
题意简(?)述:
经过上一次惨痛的经历后,郝哥痛改前非,不再卖生瓜蛋子,秤也没有了吸铁石。然而刘华强并不打算放过他。
有一个人再来买瓜。
郝哥望着那辆熟悉的摩托车,没等华强发话,便喊道:“
“给我挑亿个!”
郝哥刚转过身,看个架子上
郝哥明白,华强又来找茬了。
华强拿起了上次他劈西瓜和郝哥的刀,对郝哥说:“你这瓜要是管够我肯定要啊,那它要是不够怎么办啊?”
郝哥知道,华强刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只劈一刀,尽管华强可以以很短的时间劈开一个瓜(
郝哥不想再被劈一刀,所以他找到了你。
如果郝哥无论如何都会被劈(没有任何一种可行方案能让华强买到他想要重量的瓜)。输出 -1。
part one:
直接爆搜加一个人人都会的剪枝就行了,时间复杂度
#include<iostream>
using namespace std;
int n,a[31],ans=114514;
long long m;
void LHQ(int G,int P,int J,long long sum){
if(sum>m) return;
if(G>n){
//cout<<sum<<endl;
if(sum==m) ans=min(ans,J);
return;
}
if(P==0) sum+=a[G]*2;
else if(P==1) sum+=a[G],J++;
for(int i=0;i<3;i++) LHQ(G+1,i,J,sum);
}
int main(){
cin>>n>>m;
m*=2;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<3;i++) LHQ(1,i,0,0);
cout<<ans;
}
这个怎么样?
………外卖……萨日朗……郝哥萨日朗……
part two:
考虑折半搜索,时间复杂度
#include<iostream>
#include<unordered_map>
using namespace std;
int n,N,a[31],ans=114514;
long long m;
unordered_map <int,int> PII;
void LHQ(int G,int P,int J,long long sum){
if(sum>m) return;
if(sum==m){
PII[sum]=J;
return;
}
if(G==N+1){
if(sum<=m) PII[sum]=J;
return;
}
if(P==0) sum+=a[G]*2;
else if(P==1) sum+=a[G],J++;
for(int i=0;i<3;i++) LHQ(G+1,i,J,sum);
}
void HG(int G,int P,int J,long long sum){
if(sum>m) return;
if(sum==m){
ans=min(ans,PII[sum]+J);
return;
}
if(G==n+1){
//cout<<sum<<endl;
if(sum<=m&&PII.count(m-sum)) ans=min(ans,PII[m-sum]+J);
return;
}
if(P==0) sum+=a[G]*2;
else if(P==1) sum+=a[G],J++;
for(int i=0;i<3;i++) HG(G+1,i,J,sum);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m;
N=n/2;
m*=2;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<3;i++) LHQ(1,i,0,0);
for(int i=0;i<3;i++) HG(N+1,i,0,0);
if(ans>100000) cout<<-1;
else cout<<ans;
}
这个怎么样?
………外卖……萨日朗……郝哥萨日朗……
part three:
时间复杂度肯定是没假的,不可能比这更低了。
考虑剪枝优化。
最优性剪枝:当现在劈瓜的个数已经比目前最优的个数多了,就不用再继续搜下去了。
#include<iostream>
#include<unordered_map>
using namespace std;
int n,N,a[31],ans=114514;
long long m;
unordered_map <int,int> PII;
void LHQ(int G,int P,int J,long long sum){
if(sum>m) return;
if(sum==m){
PII[sum]=J;
return;
}
if(G==N+1){
if(sum<=m) PII[sum]=J;
return;
}
if(P==0) sum+=a[G]*2;
else if(P==1) sum+=a[G],J++;
for(int i=0;i<3;i++) LHQ(G+1,i,J,sum);
}
void HG(int G,int P,int J,long long sum){
if(sum>m||J>ans) return;
if(sum==m){
ans=min(ans,PII[sum]+J);
return;
}
if(G==n+1){
//cout<<sum<<endl;
if(sum<=m&&PII.count(m-sum)) ans=min(ans,PII[m-sum]+J);
return;
}
if(P==0) sum+=a[G]*2;
else if(P==1) sum+=a[G],J++;
for(int i=0;i<3;i++) HG(G+1,i,J,sum);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n>>m;
N=n/2;
m*=2;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<3;i++) LHQ(1,i,0,0);
for(int i=0;i<3;i++) HG(N+1,i,0,0);
if(ans==114514) cout<<-1;
else cout<<ans;
}
这个怎么样?
………外卖……萨日朗……郝哥萨日朗……
part four:
很明显不能再进一步剪枝了,我们考虑其它的优化。
优化搜索顺序:把
减少传参:容易发现当前搜索的这个瓜是否被劈不重要,只关心最后总共劈几个就行。
预处理:对
调整块长折半的长度:这个需要注意一下调完的长度可能会
卡时:这个不用我多说了吧!
#include<bits/stdc++.h>
using namespace std;
int n,N,ans=114514,m,a[32],b[32],cnt;
unordered_map <int,int> PII;
void LHQ(int G,int J,int sum){
cnt++;
if(cnt>7e6) return;
if(sum>m) return;
if(G==N+1){
if(PII[sum]) PII[sum]=min(PII[sum],J+1);
else PII[sum]=J+1;
return;
}
LHQ(G+1,J,sum+a[G]);
LHQ(G+1,J+1,sum+a[G]/2);
LHQ(G+1,J,sum);
}
void HG(int G,int J,int sum){
cnt++;
if(cnt>1.5e7){
cout<<ans<<endl;
return;
}
if(sum>m||J>ans) return;
if(sum==m){
ans=min(ans,PII[sum]+J);
return;
}
if(G==n+1){
if(PII[m-sum]) ans=min(ans,J+PII[m-sum]-1);
return;
}
HG(G+1,J,sum+a[G]);
HG(G+1,J+1,sum+a[G]/2);
HG(G+1,J,sum);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
N=n/2;
m*=2;
for(int i=1;i<=n;i++) cin>>a[i],a[i]*=2;
sort(a+1,a+1+n);
LHQ(1,0,0);
HG(N+1,0,0);
if(ans==114514) cout<<-1;
else cout<<ans;
}
这个怎么样?
满意了吧?
part five:
What's up,你是故意找茬是不是?你要不要吧!
你瞅瞅现在哪有瓜?
大棚。
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
int n,N,ans=114514,m,a[32],cnt;
long long b[32];
cc_hash_table <int,int> PII;
void LHQ(int G,int J,int sum){
if(sum>m) return;
if(sum+b[G]<m) return;
if(sum==m){
PII[sum]=J;
return;
}
if(G==N+1){
if(PII[sum]) PII[sum]=min(PII[sum],J+1);
else PII[sum]=J+1;
return;
}
LHQ(G+1,J,sum+a[G]);
LHQ(G+1,J+1,sum+a[G]/2);
LHQ(G+1,J,sum);
}
void HG(int G,int J,int sum){
if(sum>m||J>ans) return;
if(sum==m){
ans=min(ans,PII[sum]+J);
return;
}
if(G==n+1){
if(PII[m-sum]) ans=min(ans,J+PII[m-sum]-1);
return;
}
HG(G+1,J,sum+a[G]);
HG(G+1,J+1,sum+a[G]/2);
HG(G+1,J,sum);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
N=min(n/2+1,n-2);
m*=2;
for(int i=1;i<=n;i++) cin>>a[i],a[i]*=2;
sort(a+1,a+1+n);
for(int i=n;i>=1;i--) b[i]=b[i+1]+a[i];
LHQ(1,0,0);
HG(N+1,0,0);
if(ans==114514) cout<<-1;
else cout<<ans;
}
这个怎么样?
满意了吧?
写在最后:
这题从我第一次提交到第一次 AC 共提交了
upd:
感谢@waauto 对我的一些帮助。
最新代码:
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define int long long
using namespace std;
using namespace __gnu_pbds;
int n,N,ans=114514,m,a[32],cnt,b[32];
cc_hash_table <int,int> PII;
void LHQ(int G,int J,int sum){
if(sum>m) return;
if(sum+b[G]<m) return;
if(sum==m){
PII[sum]=J;
return;
}
if(G==N+1){
if(PII[sum]) PII[sum]=min(PII[sum],J+1);
else PII[sum]=J+1;
return;
}
LHQ(G+1,J,sum+a[G]);
LHQ(G+1,J+1,sum+a[G]/2);
LHQ(G+1,J,sum);
}
void HG(int G,int J,int sum){
if(sum>m||J>ans) return;
if(sum==m){
ans=min(ans,PII[sum]+J);
return;
}
if(G==n+1){
if(PII[m-sum]) ans=min(ans,J+PII[m-sum]-1);
return;
}
HG(G+1,J,sum+a[G]);
HG(G+1,J+1,sum+a[G]/2);
HG(G+1,J,sum);
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
N=min(n/2+1,n-2);
m*=2;
for(int i=1;i<=n;i++) cin>>a[i],a[i]*=2;
sort(a+1,a+1+n);
for(int i=n;i>=1;i--) b[i]=b[i+1]+a[i];
LHQ(1,0,0);
HG(N+1,0,0);
if(ans==114514) cout<<-1;
else cout<<ans;
}