题目描述
两种物品,主件 nnn 个,附件 mmm 个,每个物品都有其 sss 值和 kkk 个参数 x1,x2,…,xkx_1,x_2,\dots ,x_kx1,x2,…,xk,要求从主件和附件中各选取一个为 a,ba,ba,b,则得到的收益是 sa+sb+∑i=1k∣xai−xbi∣s_a+s_b+\sum_{i=1}^k\vert xa_i-xb_i\vertsa+sb+∑i=1k∣xai−xbi∣,求收益的最大值。数据组数 T≤100T\le 100T≤100,n,m≤100000n,m\le 100000n,m≤100000,k≤5k\le 5k≤5,0≤s≤1090\le s\le 10^90≤s≤109,∣xi∣≤109\vert x_i\vert\le 10^9∣xi∣≤109,n+m≤300000n+m\le 300000n+m≤300000。
算法分析
巧妙的思路……
可以对主件和附件分开计算贡献,即把 ∣xai−xbi∣\vert xa_i-xb_i\vert∣xai−xbi∣ 拆开,不难发现 xaixa_ixai 和 xbixb_ixbi 对答案的贡献总是相反的(xai>xbixa_i>xb_ixai>xbi 时一正一负,xai<xbixa_i<xb_ixai<xbi 时一负一正),kkk 比较小,就枚举主件每个参数对答案贡献的正负,从所有主件中计算出主件对答案贡献的最大值,与此同时,附件每个参数对答案贡献的正负情况也已对应出来,从所有附件中计算出附件对答案贡献的最大值,两者相加即可。
注意初始化为负无穷大,居然 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;
}