1 条题解

  • 0
    @ 2025-8-24 22:46:38

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ivyjiao
    复活了

    搬运于2025-08-24 22:46:38,当前版本为作者最后更新于2023-04-30 10:55:17,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    upd on 2025/7/20:攻克了 hack 数据,申请再次通过。

    已在文末给出最新代码。

    https://www.luogu.com.cn/record/225727835


    题意简(?)述:

    经过上一次惨痛的经历后,郝哥痛改前非,不再卖生瓜蛋子,秤也没有了吸铁石。然而刘华强并不打算放过他。

    有一个人再来买瓜。

    郝哥望着那辆熟悉的摩托车,没等华强发话,便喊道:“1010996110^{{10}^{-9961}} 块钱一斤!没有大棚瓜!保熟!”

    “给我挑亿个!”

    郝哥刚转过身,看个架子上 nn 个分别重 aia_i 斤的瓜,刚想挑瓜,华强又说:“今天我要请宋大哥吃答辩,得买 mm 斤西瓜,你这瓜保够吗?”

    郝哥明白,华强又来找茬了。

    华强拿起了上次他劈西瓜和郝哥的刀,对郝哥说:“你这瓜要是管够我肯定要啊,那它要是不够怎么办啊?”

    郝哥知道,华强刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只劈一刀,尽管华强可以以很短的时间劈开一个瓜(<10109961s<10^{{10}^{-9961}} s),不过他不想白费力气,他想让郝哥在 1s1s 之内告诉他最少需要劈几个瓜,否则就要劈了郝哥。

    郝哥不想再被劈一刀,所以他找到了你。

    如果郝哥无论如何都会被劈(没有任何一种可行方案能让华强买到他想要重量的瓜)。输出 -1

    part one:

    直接爆搜加一个人人都会的剪枝就行了,时间复杂度 O(3n)O(3^n)

    #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:

    考虑折半搜索,时间复杂度 O(3n2)O(3^{\frac n 2})

    #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:

    很明显不能再进一步剪枝了,我们考虑其它的优化。

    优化搜索顺序:把 aa 数组进行排序后再搜索。

    减少传参:容易发现当前搜索的这个瓜是否被劈不重要,只关心最后总共劈几个就行。

    预处理:对 aa 数组进行一个后缀和,方便判断超过下界的无解情况。

    调整块长折半的长度:这个需要注意一下调完的长度可能会 >n>n<0<0,需要避免。

    卡时:这个不用我多说了吧!

    #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 共提交了 135135 次。

    upd:149149 次,被 hack 了就不算 AC。

    感谢

    https://www.luogu.com.cn/user/355192
    的一些帮助。


    最新代码:

    #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;
    }
    
    • 1

    信息

    ID
    8669
    时间
    1000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者