LibreOJ 6559 小奇采药
题目链接:LibreOJ 6559 小奇采药
题目大意:
题解:
乍一看以为是背包模板题,但看到数据\(m\)的范围就知道背包会超时,于是考虑搜索并剪枝。
先按时间从大到小排序,求一个后缀和,\(lastw[i]\)表示\(i\)到\(n\)的时间和,\(lastv[i]\)表示\(i\)到\(n\)的价值和,然后跑\(dfs\)。
以下三种情况可以剪枝:
- 如果当前的时间和加上\(a[n].w\)还超过了背包容量,也就是不管用哪一件往背包里塞都装不下了,就退出;
- 如果当前的价值和加上\(lastv[k]\)也就是剩下的价值和仍然小于最优值,就退出;
- 如果当前的时间和加上\(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;
}