Problem J. CSGO(K维曼哈顿最大距离+二进制状态压缩(或者叫枚举二进制))
作者:互联网
这道题,如果读懂了题意,就应该知道这里的Sample Input格式有问题;但是不影响分析;
首先我觉得很多人肯定会想到,枚举,根据题目的公式找出MW里面的最大值,然后找出SW中的最大值;就可以解决了,但是我当时始终不知道枚举,我想了一种但是1e5*1e5肯定爆;
所以这个方法行不通;之后我去百度了一下这个题,我才知道,这原来是曼哈顿最远距离的变形题;
首先应该知道什么是曼哈顿距离:比如在二维坐标中的定义:d(i,j)=|xi-xj|+|yi-yj|;在几何意义上就是:
这就是曼哈顿距离;但是怎么解决多维的曼哈顿距离呢?
首先解决什么是多维:
我们二维坐标是不是(xi,yi)?三位坐标是不是(xi,yi,zi)?;那么多维我们就可以用代数形式表示为:
(x1,x2,x3,x4…xn);这就是多维上的坐标形式,只是这里的y,z写成了x2,x3的形式,所它们的本质是一样滴;(注意这里是n维的一个点)
那么这道题的后面这一坨:
不就是k维的曼哈顿距离吗?因为对于主武器Swm对应一个k维的点;所以我们在枚举的时候只需要让它们在逻辑上产生联系(其实用一个结构体就OK了或者再开别的数组);
那么怎么计算k维最大的曼哈顿距离呢?
想来看这个:我可以知道,对于二维的曼哈顿定义形式,如:
d(1,2)=|x1-x2|+|y1-y2|;可以经过下面四种讨论,我就可以把绝对值去掉:
1.如果x1-x2为正 && y1-y2为正数 那么d(1,2)=x1-x2+y1-y2;
2.如果x1-x2为正&& y1-y2为负数 那么d(1,2)=x1-x2+y2-y1;
3.如果x1-x2为负&&y1-y2为正数 那么d(1,2)=x2-x1+y1-y2;
4.如果x1-x2为负&&y-1-y2为负数 那么d(1,2)=x2-x1+y2-y1;
那么二维上的两个点之间的曼哈顿距离就只有这四种形式:
然后我们把x1和y1放在一起,x2和y2放在一起可以知道d(1,2)有下面四种取值形式:
但是哪一个最大呢?当然枚举去判断咯;
上面的形式可以发现x1和x2的系数都是相同的(+,-好都一样),同理可知:y1和y2的也是一样的;
这点说明了什么呢?说明我可以用0表示负号,1表示正号,也就是说:
可以发现都是从00—11的,然后来枚举每种状态,因为两个的点的x,y对应的状态都是相同的;
那么多维就可以类比思考;
好了,到这里这道题就解决了一大半了;
那么怎么枚举状态呢?其实如果写过用位运算求子集的应该可以想到用两个for就可以枚举出所有的状态;
这样就把本属于类的枚举转化为了一类中自己的枚举了;这样时间复杂度就成了O(2n)了;
AC代码(只不过这道题换成了n+m个k维的点了,每次枚举同一状态的最大值):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
#define INF 0x3f3f3f3f
struct MM{
ll a[20];
ll s;
}M1[maxn],M2[maxn];//两个结构体数组
int main(){
ll T,n,m,k;
scanf("%lld",&T);
while(T--){
scanf("%lld %lld %lld",&n,&m,&k);
for(int i=0;i<n;i++){//初始化第一个
scanf("%lld",&M1[i].s);//这里用结构体跟上
for(int j=0;j<k;j++){
scanf("%lld",&M1[i].a[j]);
}
}
for(int i=0;i<m;i++){//初始化第二个
scanf("%lld",&M2[i].s);//同理结构体跟上
for(int j=0;j<k;j++){
scanf("%lld",&M2[i].a[j]);
}
}
ll ans1=-INF,ans2=-INF;
ll ans=0;
for(int i=0;i<(1<<k);i++){//枚举每种对应的状态 比如 00 01 10 11这四个(把它想为二进制就好理解了)
ans1=-INF,ans2=-INF;//这里注意每次都要设为-INF因为这样才能保证每次的枚举不会被上次的影响
for(int j=0;j<n;j++){//计算主武器,在同一状态下的最大值
ll temp=0;
for(int p=0;p<k;p++){//这里就很巧妙了,利用p位移1并且能表示a[]对应的下标
if(i&(1<<p)) temp+=M1[j].a[p];//判断是不是1;
else temp-=M1[j].a[p];
}
ans1=max(ans1,temp+M1[j].s);
}
for(int j=0;j<m;j++){//计算副武器,在同一状态下的最大值
ll temp=0;
for(int p=0;p<k;p++){//这里也很巧妙
if(i&(1<<p)) temp+=M2[j].a[p];//判断是不是1;
else temp-=M2[j].a[p];
}
ans2=max(ans2,-temp+M2[j].s);//去最大值
}
ans=max(ans,ans1+ans2);
}
printf("%lld\n",ans);
}
return 0;
}
标签:x1,二进制,曼哈顿,CSGO,y1,枚举,x2,Problem,y2 来源: https://blog.csdn.net/qq_44555205/article/details/98739211