题目大意
给定 n n n种主武器和 m m m把种武器,每种武器都有一个攻击力 S S S和 k k k种副属性 a i a_i ai?。
选择一种主武器和一种副武器,使得
S m + S s + ∑ ∣ a m [ i ] ? a s [ i ] ∣ S_m + S_s + \sum |a_m[i] - a_s[i]| Sm?+Ss?+∑∣am?[i]?as?[i]∣最大化。
题目链接
思路
∑ ∣ a m [ i ] ? a s [ i ] ∣ \sum |a_m[i] - a_s[i]| ∑∣am?[i]?as?[i]∣这个东西,其实就是高纬度的曼哈顿距离。
二维曼哈顿距离是我们最常见的 ∣ x 1 ? x 2 ∣ + ∣ y 1 + y 2 ∣ |x1 - x2| + |y1 + y2| ∣x1?x2∣+∣y1+y2∣
如果我们把曼哈顿距离同属性的放到一块,再去掉绝对值,就会变成形如 ( ? x 1 + y 1 ) + ( x 1 ? y 1 ) (-x1 + y1) + (x1 - y1) (?x1+y1)+(x1?y1),二维的话,就有 4 4 4种情况, k k k维的话就有 2 k 2^k 2k种形式。
我们单独把每个武器的副属性给展开成这种 2 k 2^k 2k种形式,用 01 01 01二进制表示每个属性是 + + +还是 ? - ?,把它们全部相加,并且把攻击力加上,记为 S m [ i ] [ j ] S_m[i][j] Sm?[i][j],即第 i i i把主武器,展开形式为 j j j。
k ′ k' k′就是 k k k按位取反(对应到每个属性就是正负相反)
那么 m a x ( S m + S s + ∑ ∣ a m [ i ] ? a s [ i ] ∣ ) = m a x ( S m [ i ] [ k ] + S s [ j ] [ k ′ ] ) , i ∈ [ 1 , n ] , j ∈ [ 1. m ] max(S_m + S_s + \sum |a_m[i] - a_s[i]|) = max(S_m[i][k] + S_s[j][k']), i \in[1,n], j \in [1.m] max(Sm?+Ss?+∑∣am?[i]?as?[i]∣)=max(Sm?[i][k]+Ss?[j][k′]),i∈[1,n],j∈[1.m]
然而,遍历这个式子的时间复杂度是 O ( n m k ) O(nmk) O(nmk),所以我们想办法化简这个式子。
考虑一个简单的问题:
设有两个数组 a n , b m a_n,b_m an?,bm?,求 m a x ( a [ i ] + b [ j ] ) , i ∈ [ 1 , n ] , j ∈ [ 1. m ] max(a[i] + b[j]),i \in[1,n], j \in [1.m] max(a[i]+b[j]),i∈[1,n],j∈[1.m]。
直接硬算需要两重循环
注意到 a [ i ] + b [ j ] a[i] + b[j] a[i]+b[j]这个组合遍历到了所有 a [ i ] , a [ j ] a[i],a[j] a[i],a[j]的组合,即任意指定的 i , j i,j i,j, a [ i ] , a [ j ] a[i],a[j] a[i],a[j]都会被遍历到。
而 m a x ( a [ i ] + b [ j ] ) ≤ m a x ( a [ i ] ) + m a x ( b [ j ] ) max(a[i] + b[j]) \le max(a[i]) + max(b[j]) max(a[i]+b[j])≤max(a[i])+max(b[j]),
而 m a x ( a [ i ] ) + m a x ( b [ j ] ) max(a[i]) + max(b[j]) max(a[i])+max(b[j])这个值也一定能被遍历到,所以就有
m a x ( a [ i ] + b [ j ] ) = m a x ( a [ i ] ) + m a x ( b [ j ] ) max(a[i] + b[j]) = max(a[i]) + max(b[j]) max(a[i]+b[j])=max(a[i])+max(b[j])
那么这么算就从两重循环变成了两个循环。
对应到这个题目中, m a x ( S m [ i ] [ k ] + S s [ j ] [ k ′ ] ) , i ∈ [ 1 , n ] , j ∈ [ 1. m ] max(S_m[i][k] + S_s[j][k']), i \in[1,n], j \in [1.m] max(Sm?[i][k]+Ss?[j][k′]),i∈[1,n],j∈[1.m],实际上也遍历到了所有组合(注意合法组合一定比不合法的大,比如|5 - 2|的合法组合是 + 5 , ? 2 +5,-2 +5,?2,那么它就比不合法组合 ? 5 , + 2 -5,+2 ?5,+2要大)。
所以上面式子也可以继续化简:
m a x ( S m [ i ] [ k ] + S s [ j ] [ k ′ ] ) = m a x ( S m [ i ] [ k ] ) + m a x ( s s [ j ] [ k ′ ] ) max(S_m[i][k] + S_s[j][k']) = max(S_m[i][k]) + max(s_s[j][k']) max(Sm?[i][k]+Ss?[j][k′])=max(Sm?[i][k])+max(ss?[j][k′])
而对于每个 k k k,我们只需要得到它的最大值,所以 i i i这一维也可以省去,我们 S m [ k ] = m a x ( S m [ i ] [ k ] ) S_m[k] = max(S_m[i][k]) Sm?[k]=max(Sm?[i][k])
那么上式子可以再度化简为:
m a x ( S m [ k ] + S s [ k ′ ] ) max(S_m[k] + S_s[k']) max(Sm?[k]+Ss?[k′])
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;int T, n, m, k, s[8], x;const long long INF = 0x7f7f7f7f;long long s1[1 <<11], s2[1 << 11];int main()
{
scanf("%d", &T);while(T--) {
for(int i = 0; i < 1 << 11; ++i)s1[i] = s2[i] = -INF;scanf("%d%d%d", &n, &m, &k);for(int i = 1; i <= n; ++i) {
scanf("%d", &x);for(int j = 1; j <= k; ++j)scanf("%d", &s[j]);for(int j = 0; j < 1 << k; ++j) {
long long sum = x;for(int l = 1; l <= k; ++l) {
if((j >> (l - 1)) & 1)sum += s[l];elsesum -= s[l];}s1[j] = max(s1[j], sum);}}for(int i = 1; i <= m; ++i) {
scanf("%d", &x);for(int j = 1; j <= k; ++j)scanf("%d", &s[j]);for(int j = 0; j < 1 << k; ++j) {
long long sum = x;for(int l = 1; l <= k; ++l) {
if((j >> (l - 1)) & 1)sum -= s[l];//这里我直接反着存,相当于直接取反了elsesum += s[l];}s2[j] = max(s2[j], sum);}}long long ans = 0;for(int i = 0; i <= 1 << k; ++i)ans = max(ans, s1[i] + s2[i]);printf("%lld\n", ans);}return 0;
}