《算法竞赛进阶指南》送礼物

    科技2022-09-02  103

    送礼物

    达达帮翰翰给女生送礼物,翰翰一共准备了N个礼物,其中第i个礼物的重量是G[i]。

    达达的力气很大,他一次可以搬动重量之和不超过W的任意多个物品。

    达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。

    输入格式 第一行两个整数,分别代表W和N。

    以后N行,每行一个正整数表示G[i]。

    输出格式 仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。

    数据范围 1≤N≤46, 1≤W,G[i]≤231−1 输入样例: 20 5 7 5 4 18 1 输出样例: 19

    首先本题使用dfs算法寻找数据的所有组合,挑选其中合法的最大的值。

    如果是简单的暴搜的话,2^46的搜索量的话一定会超时的,我们这里利用一个技巧:

    先对前半部分进行搜索将得到存储到数组中;

    然后在对后半部分进行搜索,在搜索结束时,在利用二分的方法在前半部分中寻找合法且最大的值。这样O(2^23)+O( 2 ^23)*O(log(n/2))的数据量会明显降低数据搜索量

    #include <iostream> #include <algorithm> using namespace std; const int N=1<<24; typedef long long LL; int weights[N],g[50]; int n,m,k; int cnt=0; int ans=0; void dfs(int u,int s) {//u为当前搜索深度,s为当前存储量 if(u==k) {//当达到我们的目标的搜索深度时,储存我们当前的数据 weights[cnt++]=s; return; } if((LL)s+g[u]<=m) dfs(u+1,s+g[u]);//当可以拿起拿起当前数据进行下一个分支的话我们进入下一个分支 dfs(u+1,s);//我们直接选择不拿起当前数据,进入下一个分支 //这样我们就能把所有的位置都遍历到 } void dfs2(int u,int s) { if(u==n) {//当我们后半部分搜索完毕时,通过二分来与前半部分进行合法的组合 int l=0,r=cnt-1; while(l<r) { int mid=l+r+1>>1; if((LL)s+weights[mid]<=m)l=mid; else r=mid-1; } if((LL)s+weights[l]<=m)ans=max(ans,s+weights[l]); return; } if((LL)s+g[u]<=m)dfs2(u+1,s+g[u]); dfs2(u+1,s); } int main() { cin>>m>>n; for(int i=0;i<n;i++) cin>>g[i]; sort(g,g+n); reverse(g,g+n); k=n/2+2; //如果直接暴力搜索的话是2^46的搜索复杂度的,时间肯定会超时 //这里我们用到的思想是先对前一般的数据进行暴搜,将得到的小于m的所有结果存储到weights数组中 //然后在对另一半的数据进行搜索,在搜索结束时,在利用二分的方法查找所有合法的结果,每次与答案比较,得到最有解 dfs(0,0);//对前一般数据进行搜索 //我们对得到的前半部分所有组合的合法的数据进行排序、判重 sort(weights,weights+cnt); int t=1; for(int i=0;i<cnt;i++) if(weights[i]!=weights[i-1]) weights[t++]=weights[i]; dfs2(k,0); cout<<ans<<endl; return 0; }
    Processed: 0.008, SQL: 10