首先需要知道视角就是平时理解的视角(角度),题目中说垂直河岸容易误导。然后需要注意的是看到完整的船只。
思路:对于每条船,能观察到它完整的观测点区间为(y-z,x+z),把区间存储。接下来的排序比较关键:先按点的位置从小到大排,如果位置相同则前端点排前面(对于向右船只的区间,左端点为前端点;向右的区间,右端点为前端点。 这样算出来的才能是区间重叠的最大值,这里需要想一想),如果前面二者都相等,则向右的排前面(此时对应相向而行,必然会相遇)。最后再用前缀和思想先从左向右计算向右区间中每个点之前覆盖的最大值ans[i],然后再从右向左计算向左区间中(每个点对应区间覆盖值j+ans[i]),比较出最大值即可。
注意数组一定要开足够大,不然会TLE,原来数组越界也可能TLE。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<iostream>
#include<vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+50;
int ans[maxn],n;///ans[i]记录1到i的最多的观测向右的船只的区间覆盖
struct node{
int end_type,dir,end_loc;
node(int tt,int dd,int ll):end_type(tt),dir(dd),end_loc(ll){
}
};
vector<node> vec;
bool cmp(node a,node b){
if(a.end_loc!=b.end_loc) return a.end_loc<b.end_loc;
if(a.end_type!=b.end_loc) return a.end_type<b.end_type;///因为找覆盖次数最大的区间,所以起点先放前面
return a.dir>b.dir;///右边的放前面
}
int main(){
int t;
while(scanf("%d",&t)!=EOF){
int casenum=0;
while(t--){
vec.clear();
vec.push_back(node(inf,inf,inf));///往后空一位
int x,y,z,d,left_end,right_end;
//memset(ans,0,sizeof(ans));
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d%d%d%d",&x,&y,&z,&d);
left_end=y-z;
right_end=x+z;
if(left_end<=right_end){
vec.push_back(node(1,d,left_end));
vec.push_back(node(2,d,right_end));
}
}
sort(++vec.begin(),vec.end(),cmp);
ans[0]=0;
int temp=0;///temp记录到某个位置此时的区间覆盖
for(int i=1;i<vec.size();i++){///从左往右找向右的区间
if(vec[i].dir==1){
if(vec[i].end_type==1) temp++;///如果是左端点自加
if(vec[i].end_type==2) temp--;///右端点自减
}
ans[i]=max(temp,ans[i-1]);
}
temp=0;
int t=0,f=0;
for(int i=vec.size()-1;i>0;i--)///这里需要反过来,因为先走右端点 从右往左找向左的区间
if(vec[i].dir==-1){
if(vec[i].end_type==2) temp++;
t=max(temp,t);///到目前的最大区间覆盖值
f=max(f,t+ans[i]);
if(vec[i].end_type==1) temp--;
}
printf("Case #%d:\n%d\n",++casenum,f);
}
}
return 0;
}