去郊游 (01dfs+二分)

题目大意:每个物品有自己的体积和价值,要求体积xi不超过m的情况下获得的最大价值W,(w,x特别大)

输入:
第一行两个整数n,m。
接下来n行每行两个整数,xi,wi,表示第i个物品的体积和价值。
2≤n≤40
0≤m≤10^18
0≤xi,wi≤10^15

输出:
一行一个整数表示最大价值。

分析:
这题的体积x和价值w的范围非常大所以用01背包的板子数组开不下,看到n非常小,考虑dfs,但是n最大40,2^40也比较大,dfs一般1e6多会爆,2的20次是1,048,576刚好1e6多一点,可以分两次来做。

第一遍dfs把前n/2个物品的选和不选的情况枚举出来,存到一个结构体数组o中,o里面存体积x以及x下获得的价值

然后扫一遍o[i].w = max(o[i].w,w[i-1].w)使得第二遍二分搜索m-sumx的价值是最大价值

第二遍dfs也像第一遍一样枚举选和不选,在退出时,二分搜索第一个小于等于m-sumx体积的最大价值+当前的sumw,更新ans最大值。

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

template <typename T>
inline T read() {
    T sum = 0, fl = 1;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) 
        if (ch == '-') fl = -1;
    for (; isdigit(ch); ch = getchar()) 
        sum = sum * 10 + (ch - '0');
    return sum * fl;
}


template <typename T>
inline void write(T x) {
    static int sta[45];
    int cnt = 0;
    if (x < 0) putchar('-'), x = -x;
    do {
        sta[cnt++] = x % 10, x /= 10;
    } while (x);
    while (cnt) putchar(sta[--cnt] + '0');
}

const int maxn = 1e6+10;
int n;
i64 m;
i64 ans,x[50],w[50];
struct ty{
    i64 x,w;
    bool operator<(const ty &a)const{
        return x<a.x;
    }
}o[maxn<<2];
int tot;
void dfs1(int cnt,i64 sumx,i64 sumw){
    if(sumx>m) return;
    if(cnt==n/2+1){
        o[++tot]={sumx,sumw};
        return;
    }
    dfs1(cnt+1,sumx,sumw);
    dfs1(cnt+1,sumx+x[cnt],sumw+w[cnt]);
}

void dfs2(int cnt,i64 sumx,i64 sumw){
    if(sumx>m) return;
    if(cnt==n/2){
        int pos = upper_bound(o+1,o+tot+1,ty{m-sumx,sumw})-o-1;//第一个小于等于的元素
        ans = max(ans,o[pos].w+sumw);
        return;
    }
    dfs2(cnt-1,sumx,sumw);
    dfs2(cnt-1,sumx+x[cnt],sumw+w[cnt]);
}
int main(){
    n = read<int>();
    m = read<i64>();
    for(int i = 1;i<=n;++i){
        x[i] = read<i64>();
        w[i] = read<i64>();
    }
    dfs1(1,0,0);
    sort(o+1,o+tot+1);
    for(int i = 1;i<=tot;++i){
        o[i].w = max(o[i].w,o[i-1].w);
    }
    dfs2(n,0,0);
    write<i64>(ans);
    return 0;
}

c++STL upper_bound()返回第一个大于的元素的索引,再减一就是小于等于了,结构体的二分我也学到了,手搓也不是不行就是容易写错

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值