送礼物
达达帮翰翰给女生送礼物,翰翰一共准备了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;
}