题目大意
平面上有n个点,从原点出发,每次的路径长度要严格小于上一次,求能访问的点的最大值(可以重复访问)
分析
将边按照边权排序,从大到小依次处理,这样就可以满足处理某条边时,之前的边都可以访问,
设当前处理的边的两个端点分别为
x,y(x<y)
maxi(i)
表示以i为终点的路径最大值。
不难得到
maxi(x)=max(maxi(x),maxi(y)+1)
同理,
maxi(y)=max(maxi(y),maxi(x)+1)
接下来有两个问题:
1、这样得到的答案是从任意一点出发,而题目要求从原点出发
2、对于权值相同的边,计算会出问题(严格小于)
对于问题1,我们加几个特判即可:(设原点为0号点)
若
maxi(x)=0,且x≠0,maxi(y)不能转移
,y也同理。
若
x=0
,那么
maxi(y)=max(maxi(y),1)
对于问题2,只需要用类似于滚动的方法来处理即可(详见代码)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 2010
using namespace std;
int maxi[MAXN],maxir[MAXN],cnt,n,len[MAXN*MAXN];
pair<int,int> a[MAXN*MAXN],l[MAXN];
vector<int> up;
long long squ(long long x){
return x*x;
}
long long lenth(int x,int y){
return squ(l[x].first-l[y].first)+squ(l[x].second-l[y].second);
}
bool cmp(pair<int,int> x,pair<int,int> y){
return lenth(x.first,x.second)>lenth(y.first,y.second);
}
int main(){
SF("%d",&n);
for(int i=1;i<=n;i++)
SF("%d%d",&l[i].first,&l[i].second);
for(int i=0;i<=n;i++)
for(int j=i+1;j<=n;j++)
a[++cnt]=make_pair(i,j);
sort(a+1,a+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
len[i]=lenth(a[i].first,a[i].second);
for(int i=1;i<=cnt;i++){
int u=a[i].first;
int v=a[i].second;
if(len[i]!=len[i-1]){
for(int j=0;j<up.size();j++)
maxi[up[j]]=max(maxi[up[j]],maxir[up[j]]);
up.clear();
}
up.push_back(u);
up.push_back(v);
if(u==0){
maxir[v]=max(maxi[v],1);
continue;
}
if(maxi[u]!=0)
maxir[v]=max(maxir[v],maxi[u]+1);
if(maxi[v]!=0)
maxir[u]=max(maxir[u],maxi[v]+1);
}
for(int j=0;j<up.size();j++)
maxi[up[j]]=max(maxi[up[j]],maxir[up[j]]);
up.clear();
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,maxi[i]);
PF("%d",ans);
}