【HDU】6435 CSGO 求K维曼哈顿距离下距离最大点对

题目大意

给定 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| x1x2+y1+y2
如果我们把曼哈顿距离同属性的放到一块,再去掉绝对值,就会变成形如 ( − x 1 + y 1 ) + ( x 1 − y 1 ) (-x1 + y1) + (x1 - y1) (x1+y1)+(x1y1),二维的话,就有 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];
                    else
                        sum -= 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];//这里我直接反着存,相当于直接取反了
                    else
                        sum += 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;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值