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

送礼物

达达帮翰翰给女生送礼物,翰翰一共准备了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;    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值