题目
题目描述
垃圾
J
Z
M
\tt JZM
JZM 不适合继续学习
O
I
OI
OI 了。他到了离开的时候了。这是他的退役赛。
这场退役赛有两支队伍。队伍的综合评定分数,等于队伍中每个成员的分数之和。公平起见,两支队伍的综合评定分数必须相等。
每个人的出现都会带来比赛热度的影响,最终热度为每个人的影响力之和。我们当然要让最终热度最大。
现在给出两支队伍的候选人,每个队伍都可以从自己那一方的候选人中选任意多个人。求出最终热度的最大值。
数据范围与提示
两支队伍分别的候选人数量
n
,
m
n,m
n,m 均不超过
1
0
3
10^3
103 。
每个候选人的综合评定分数 w ≤ 1 0 3 w\le 10^3 w≤103 ,影响力 v v v 在 − 1 0 9 -10^9 −109 到 1 0 9 10^9 109 之间。
人生就像打拳击。你打倒一个又一个对手,最终站上冠军的王座。最终你被别人打败,新的时代,又会开始。
思路
可以把第二支队伍中的候选人的能力值看成负数,问题转化为选择为 0 0 0 的子集。
仔细想想,如果我们一个选负、一个选正,交替出现,那么权值和始终在 [ − 1 0 3 , 1 0 3 ] [-10^3,10^3] [−103,103] ,就可以背包乱来。
尽管不可能有那么好的情况,我们也可以引入这个思想,随机打乱原序列,然后把背包的容量适当调小。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void getMax(int_ &a,const int_ &b){
(a < b ? a = b : 0); return ;
}
const int MaxN = 1000;
const int M = 100000;
const int_ infty = (1ll<<61)-1;
struct Node{
int w, v;
};
Node node[MaxN<<1];
int_ dp[M+1];
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
srand(5201314);
int n = readint(), m = readint();
for(int i=0; i<n; ++i){
node[i].w = readint();
node[i].v = readint();
}
for(int i=n; i<n+m; ++i){
node[i].w = -readint();
node[i].v = readint();
}
/* random_suffle */ ;
for(int i=0; i<n+m; ++i){
int x = rand()%(n+m-i);
swap(node[i],node[i+x]);
}
/* do dp once */ ;
for(int i=0; i<=M; ++i)
dp[i] = -infty;
dp[M>>1] = 0;
for(int i=0; i<n+m; ++i){
if(node[i].w < 0)
for(int j=0; j-node[i].w<=M; ++j)
getMax(dp[j],dp[j-node[i].w]+node[i].v);
if(node[i].w > 0)
for(int j=M; j>=node[i].w; --j)
getMax(dp[j],dp[j-node[i].w]+node[i].v);
}
printf("%lld\n",dp[M>>1]);
return 0;
}