LibreOJ 6559 小奇采药

题目链接:LibreOJ 6559 小奇采药

题目大意:

题解:
乍一看以为是背包模板题,但看到数据\(m\)的范围就知道背包会超时,于是考虑搜索并剪枝。
先按时间从大到小排序,求一个后缀和,\(lastw[i]\)表示\(i\)\(n\)的时间和,\(lastv[i]\)表示\(i\)\(n\)的价值和,然后跑\(dfs\)

以下三种情况可以剪枝:

  1. 如果当前的时间和加上\(a[n].w\)还超过了背包容量,也就是不管用哪一件往背包里塞都装不下了,就退出;
  2. 如果当前的价值和加上\(lastv[k]\)也就是剩下的价值和仍然小于最优值,就退出;
  3. 如果当前的时间和加上\(lastw[k]\)也就是剩下的时间和仍然小于背包容量,就拿当前的价值和加上\(lastv[k]\)和原最优解比较,如果当前的更优,更新答案,这时候就没有再递归下去的必要了,直接退出。
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long

ll ans = 0, m, lastw[155], lastv[155];
int n, t;

struct node {
    int w, v;
    bool operator<(const node &obj) const { return w > obj.w; }
} a[155];

void dfs(int k, ll sumw, ll sumv) {
    ans = max(ans, sumv);
    if (sumw + a[n].w > m || sumv + lastv[k] <= ans) {
        return;
    }
    if (sumw + lastw[k] <= m) {
        ans = max(ans, sumv + lastv[k]);
        return;
    }
    if (sumw + a[k].w <= m) {
        dfs(k + 1, sumw + a[k].w, sumv + a[k].v);
    }
    dfs(k + 1, sumw, sumv);
}

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d%lld", &n, &m);
        for (int i = 1; i <= n; ++i) {
            scanf("%d%d", &a[i].w, &a[i].v);
        }
        sort(a + 1, a + n + 1);
        lastw[n + 1] = 0;
        lastv[n + 1] = 0;
        for (int i = n; i >= 1; i--) {
            lastw[i] = lastw[i + 1] + a[i].w;
            lastv[i] = lastv[i + 1] + a[i].v;
        }
        ans = 0;
        dfs(1, 0, 0);
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2022-01-29 21:18  ZZHHOOUU  阅读(39)  评论(0编辑  收藏  举报