蓝书(算法竞赛进阶指南)刷题记录——CH2401送礼物(双向dfs+二分)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/89422276

题目:CH2401.
题目大意:给定 n n 个物品的重量 a [ i ] a[i] 以及一个上限 W W ,要求挑选一些物品使得 a [ i ] a[i] 之和小于等于 W W 且最接近 W W .
1 n 45 , W < 2 31 1\leq n\leq 45,W<2^{31} .

看起来是道背包模板题,但是由于 W W 太大只能考虑搜索了.

很容易发现搜索是 O ( 2 n ) O(2^n) 的,复杂度太高,但是可以发现 O ( 2 n 2 ) O(2^{\frac{n}{2}}) 级别的算法是可以通过这道题的,所以考虑双向搜索.

考虑把礼物分成两部分,第一部分把所有可能得到的 a [ i ] a[i] 和放入一个数组中排序,第二部分直接大力搜索,每次凑出一种情况,就把当前的 a [ i ] a[i] 和与第一部分中的一个组合,可以通过二分实现.

时间复杂度 O ( n 2 n 2 ) O(n2^{\frac{n}{2}}) .

在具体实现的过程中,可以把 a [ i ] a[i] 从大到小排序,并且由于第二部分的复杂度多了 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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/89422276