2020 hdu多校4 1003 Contest of Rope Pulling(01背包+随机化)
题意
\(T\)组数据,有\(n\),\(m\)代表每个班级的人数,每个人有两种属性\(w_i\)(力量值),\(v_i\)(魅力值),问你从两班中选择两个子集(可为空),使得两个子集的力量值和相等,求选出来的所有人的魅力值和的最大值
解法
可以将两组人合并为一组,这里只需要将第二组的力量值改成负数就行。之后我们在dp数组上进行01背包,\(dp[0]\),就是答案。不过因为dp的时候下标可能为负数,所以需要加上一个基值代表\(0\)。
因为我们所求的是恰好背包装到\(0\)的值,所以需要将整个dp数组初始化为\(-INF\), 并且每次dp的时候确保\(dp[j - w[i]]\)不为\(-INF\)(这么做可以确保dp到j的时候前面都是确实加过其他人的魅力值,而不是空的)。另外得注意一下\(w[i]\)为负数时dp的范围与方向,这只要确保\(dp[j - w[i]]\)值的更新在\(dp[j]\)之后就行,范围只要不超过数组的范围就行。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 3e3 + 7;
const ll M = 1e5 + 7;
const ll INF = 1e18;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int t;
int n, m;
struct node{
ll w, v;
};
node a[N];
ll dp[M];
ll getdp(int n)
{
fill(dp, dp + M, -INF);
dp[50000] = 0;
for (int i = 1; i <= n;i++)
{
if(a[i].w >= 0)
{
for (int j = 1e5; j >= a[i].w;j--)
{
if(dp[j - a[i].w] != -INF) dp[j] = max(dp[j], dp[j - a[i].w] + a[i].v);
}
}
else
{
int lim = 1e5 + a[i].w;
for (int j = 0; j <= lim; j++)
{
if (dp[j - a[i].w] != -INF) dp[j] = max(dp[j], dp[j - a[i].w] + a[i].v);
}
}
}
return dp[50000];
}
int main()
{
srand(time(0));
t = read();
//scanf("%d", &t);
while(t--)
{
n = read(), m = read();
//scanf("%d %d", &n, &m);
for (int i = 1; i <= n + m;i++)
{
//scanf("%lld %lld", &a[i].w, &a[i].v);
a[i].w = read(), a[i].v = read();
if(i > n) a[i].w = -a[i].w;
}
random_shuffle(a + 1, a + n + m + 1);
ll ans = 0;
ans = getdp(n + m);
printf("%lld\n", ans);
}
return 0;
}