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

题目: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 &lt; 2 31 1\leq n\leq 45,W&lt;2^{31} 1n45,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值