题目链接
题目:
Takahashi will participate in a programming contest, which lasts for T minutes and presents N problems.
With his extrasensory perception, he already knows that it will take Ai minutes to solve the i-th problem.
He will choose zero or more problems to solve from the N problems so that it takes him no longer than T minutes in total to solve them.
Find the longest possible time it takes him to solve his choice of problems.
数据范围:
All values in input are integers.
1 ≤ N ≤40
1 ≤ T ≤ 1e9
1 ≤ Ai ≤ 1e9
样例:
Sample Input 1
5 17
2 3 5 7 11
Sample Output 1
17
Sample Input 2
6 100
1 2 7 5 8 10
Sample Output 2
33
Sample Input 3
6 100
101 102 103 104 105 106
Sample Output 3
0
7 273599681
6706927 91566569 89131517 71069699 75200339 98298649 92857057
Sample Output 4
273555143
思路:数据范围太大,显然无法用01背包来做,我们来看N的范围,考虑一下可以用搜索,如果单纯暴搜2的40次方复杂度太高,这里用到了一种叫做meet in the middle的优化,也叫折半搜索。
[1]将所有物品按重量从大到小排序
[2]现将前N/2件物品凑出的所有重量打表,然后排序并判重
[3]搜索剩下的N/2件物品能凑出的重量,然后在表中二分出不超过T的最大值
下面是AC代码。
// An highlighted block
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 41,M= 1 << 21;
int n,m,k;
int w[N];
int weight[M];
int ans;
int cnt;
void dfs1(int u,int s)
{
if(u==k){
weight[cnt++]=s;
return;
}
dfs1(u+1,s);
if((ll)s+w[u]<=m) dfs1(u+1,s+w[u]);
}
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)weight[mid]+s<=m) l=mid;
else r=mid-1;
}
if((ll)weight[l]+s<=m) ans=max(ans,weight[l]+s);
return;
}
dfs2(u+1,s);
if((ll)s+w[u]<=m) dfs2(u+1,s+w[u]);
}
int main()
{
cin >> n >> m;
for(int i=0;i<n;i++) cin >> w[i];
sort(w,w+n);
reverse(w,w+n);
k=n/2;
dfs1(0,0); //搜索前面N/2
sort(weight,weight+cnt);
cnt=unique(weight,weight+cnt)-weight; //去重
dfs2(k,0); //搜索后面N/2
cout <<ans;
return 0;
}