题目描述
两种物品,主件 n n n 个,附件 m m m 个,每个物品都有其 s s s 值和 k k k 个参数 x 1 , x 2 , … , x k x_1,x_2,\dots ,x_k x1,x2,…,xk,要求从主件和附件中各选取一个为 a , b a,b a,b,则得到的收益是 s a + s b + ∑ i = 1 k ∣ x a i − x b i ∣ s_a+s_b+\sum_{i=1}^k\vert xa_i-xb_i\vert sa+sb+∑i=1k∣xai−xbi∣,求收益的最大值。数据组数 T ≤ 100 T\le 100 T≤100, n , m ≤ 100000 n,m\le 100000 n,m≤100000, k ≤ 5 k\le 5 k≤5, 0 ≤ s ≤ 1 0 9 0\le s\le 10^9 0≤s≤109, ∣ x i ∣ ≤ 1 0 9 \vert x_i\vert\le 10^9 ∣xi∣≤109, n + m ≤ 300000 n+m\le 300000 n+m≤300000。
算法分析
巧妙的思路……
可以对主件和附件分开计算贡献,即把 ∣ x a i − x b i ∣ \vert xa_i-xb_i\vert ∣xai−xbi∣ 拆开,不难发现 x a i xa_i xai 和 x b i xb_i xbi 对答案的贡献总是相反的( x a i > x b i xa_i>xb_i xai>xbi 时一正一负, x a i < x b i xa_i<xb_i xai<xbi 时一负一正), k k k 比较小,就枚举主件每个参数对答案贡献的正负,从所有主件中计算出主件对答案贡献的最大值,与此同时,附件每个参数对答案贡献的正负情况也已对应出来,从所有附件中计算出附件对答案贡献的最大值,两者相加即可。
注意初始化为负无穷大,居然 1A 了,感动……
代码实现
#include <cstdio>
#include <climits>
#include <algorithm>
typedef long long int ll;
const int maxn=100005;
int as[maxn],ax[maxn][5],bs[maxn],bx[maxn][5];
int main() {
int t;scanf("%d",&t);
while(t--) {
int n,m,K;scanf("%d%d%d",&n,&m,&K);
for(register int i=0;i<n;++i) {
scanf("%d",&as[i]);
for(register int j=0;j<K;++j) scanf("%d",&ax[i][j]);
}
for(register int i=0;i<m;++i) {
scanf("%d",&bs[i]);
for(register int j=0;j<K;++j) scanf("%d",&bx[i][j]);
}
ll ans=LONG_LONG_MIN;
for(register int i=0;i<(1<<K);++i) {
ll mxa=LONG_LONG_MIN;
for(register int j=0;j<n;++j) {
ll now=as[j];
for(register int k=0;k<K;++k) now+=((i>>k&1)?1:-1)*ax[j][k];
mxa=std::max(mxa,now);
}
ll mxb=LONG_LONG_MIN;
for(register int j=0;j<m;++j) {
ll now=bs[j];
for(register int k=0;k<K;++k) now+=((i>>k&1)?-1:1)*bx[j][k];
mxb=std::max(mxb,now);
}
ans=std::max(ans,mxa+mxb);
}
printf("%lld\n",ans);
}
return 0;
}