题目:CH2401.
题目大意:给定
n
n
n个物品的重量
a
[
i
]
a[i]
a[i]以及一个上限
W
W
W,要求挑选一些物品使得
a
[
i
]
a[i]
a[i]之和小于等于
W
W
W且最接近
W
W
W.
1
≤
n
≤
45
,
W
<
2
31
1\leq n\leq 45,W<2^{31}
1≤n≤45,W<231.
看起来是道背包模板题,但是由于 W W W太大只能考虑搜索了.
很容易发现搜索是 O ( 2 n ) O(2^n) O(2n)的,复杂度太高,但是可以发现 O ( 2 n 2 ) O(2^{\frac{n}{2}}) O(22n)级别的算法是可以通过这道题的,所以考虑双向搜索.
考虑把礼物分成两部分,第一部分把所有可能得到的 a [ i ] a[i] a[i]和放入一个数组中排序,第二部分直接大力搜索,每次凑出一种情况,就把当前的 a [ i ] a[i] a[i]和与第一部分中的一个组合,可以通过二分实现.
时间复杂度 O ( n 2 n 2 ) O(n2^{\frac{n}{2}}) O(n22n).
在具体实现的过程中,可以把 a [ i ] a[i] a[i]从大到小排序,并且由于第二部分的复杂度多了 O ( n ) O(n) O(n),所以可以给第一部分多分配一点来到达优化的效果.
还有一点要注意,这道题两个int相加会爆int…
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
typedef unsigned int UI;
const int N=45;
int tx,n;
UI w,x[1<<N/2+3],a[N+9],mx;
bool cmp(const UI &a,const UI &b){return a>b;}
void dfs1(int k,UI s){
if (s>w) return;
if (k>n/2+2) {x[++tx]=s;return;} //由于第二个搜索复杂度多个O(n),所以前面搜多一点会更快
dfs1(k+1,s);
dfs1(k+1,s+a[k]);
}
UI Upper(int k){
int l=0,r=tx,mid=l+r+1>>1;
for (;l<r;mid=l+r+1>>1)
k<x[mid]?r=mid-1:l=mid;
return x[l];
}
void dfs2(int k,UI s){
if (s>w) return;
if (k>n) {mx=max(mx,s+Upper(w-s));return;}
dfs2(k+1,s);
dfs2(k+1,s+a[k]);
}
Abigail into(){
scanf("%u%d",&w,&n);
for (int i=1;i<=n;++i)
scanf("%u",&a[i]);
}
Abigail work(){
sort(a+1,a+1+n,cmp); //貌似从大到小排序会快一点
dfs1(1,0);
sort(x+1,x+1+tx);
mx=x[tx];
dfs2(n/2+3,0);
}
Abigail outo(){
printf("%u\n",mx);
}
int main(){
into();
work();
outo();
return 0;
}