2020 Multi-University Training Contest 4 1012 Last Problem(01背包)
题目
http://acm.hdu.edu.cn/showproblem.php?pid=6804
题意
分别给出 n,m 对数(w,v),在 n 对 和 m 对中分别取出若干对使得 w 的和相等,在这个前提下使所有的 v 之和最大,问 v 最大值为多少。
题解
首先看到这个题意,第一时间想到对 n 和 m 分别跑一遍01背包最后在枚举 w 的值取出最大的那个,可是这样的话我们会发现跑一遍01背包就需要 1e9,时间上过不去。所以我们先将 m 对数的 w 取反,然后将 n 和 m 对数放一起同时跑,答案就是 dp[0] 了。为了将时间缩小我们将 n 和 m 对数放在一起随机打乱,使得数值能够在 0 附近震荡,在这种情况下当绝对数很大时能恢复到 0 的概率很小,所以就能将原来 1e6 的 dp 数组减小,只在 1e5 的范围内更新就好。
#include <bits/stdc++.h>
// #include <iostream>
// #include <cstring>
// #include <string>
// #include <algorithm>
// #include <cmath>
// #include <cstdio>
// #include <queue>
// #include <stack>
// #include <map>
// #include <bitset>
// #include <set>
// #include <vector>
// #include <iomanip>
#define ll long long
#define ull unsigned long long
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define bep(i, a, b) for(int i = a; i >= b; --i)
#define lowbit(x) (x&(-x))
#define MID (l + r) / 2
#define ls pos*2
#define rs pos*2+1
#define pb push_back
#define ios() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 1e6 + 1010;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
struct node {
ll w, v;
}arr[maxn];
ll dp[maxn], tail;
int main() {
srand(time(0));
int T;
scanf("%d", &T);
while(T--) {
rep(i, 0, maxn - 1) dp[i] = -INF;
int n, m;
scanf("%d%d", &n, &m);
tail = 0;
rep(i, 1, n) {
tail++;
scanf("%lld%lld", &arr[tail].w, &arr[tail].v);
}
rep(i, 1, m) {
tail++;
scanf("%lld%lld", &arr[tail].w, &arr[tail].v);
arr[tail].w = -arr[tail].w;
}
random_shuffle(arr + 1, arr + 1 + tail);
dp[100000] = 0;
rep(i, 1, tail) {
if(arr[i].w > 0) {
bep(j, 200000, arr[i].w) {
dp[j] = max(dp[j], dp[j - arr[i].w] + arr[i].v);
}
}
if(arr[i].w < 0) {
rep(j, 0, 200000 + arr[i].w) {
dp[j] = max(dp[j], dp[j - arr[i].w] + arr[i].v);
}
}
}
printf("%lld\n", dp[100000]);
}
return 0;
}