2022暑期牛客多校训练第5场 A.Don‘t Starve

文章目录

  • 原题链接
  • 题目大意
  • 题解
  • 参考代码

原题链接

                https://ac.nowcoder.com/acm/contest/33190/A

题目大意

在二维平面上,有 n ( 1 ≤ n ≤ 2000 ) n(1 \leq n \leq 2000) n(1n2000) 个位置有食物。从原点出发,每次直线前往其他任意一个有食物的位置收集食物。收集完后再次前往下一个点。每当离开一个有食物的点后,该点的食物最后刷新。并且每次的移动距离必须严格下降。
求最多可以收集到多少食物。

题解

由题目得,所经过的路径需要递减,如果直接考虑状态的话本蒟蒻难以下手,
根据“正难则反”,我们考虑一下反着思考,只需要最后一个点是原点即可,思路就清晰多了。
我们只需要从大到小将边长进行排序,尽可能选择较大的边遍历即可。
如果有相同边长的边,我们需要将其共同进行储存结果计算即可,再进行新的计算。
代码的复杂度在 O ( n 2 l o g n 2 ) O(n^2logn^2) O(n2logn2) sort牛逼
边长的计算需要用欧拉公式记录。

参考代码

#include
#define FOR(i,n,m) for(int i=n;i<=m;++i)
using namespace std;
template<class T> inline void read(T&x)
{
    x=0;
    char c=getchar(),last=' ';
    while(!isdigit(c))
        last=c,c=getchar();
    while(isdigit(c))
        x=x*10+c-'0',c=getchar();
    x=last=='-'?-x:x;
}
const int N=2e3+5;
int x[N],y[N];
int f[N],g[N];
int n,cnt;
struct node{
	int l,r,w;
}a[N*N];
bool cmp(node i,node j)
{
	return i.w<j.w;
}
inline int get(int a,int b)           //防止精度误差不开根号
{
	return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]);
}
int main()
{
	read(n);
	FOR(i,1,n)
	{
		read(x[i]);
        read(y[i]);
		f[i]=1;
		a[cnt++]={0,i,get(0,i)};
	}
	FOR(i,1,n)
		FOR(j,1,n)
		{
			if(i==j)
				continue;
			a[cnt].l=i;
			a[cnt].r=j;
			a[cnt++].w=get(i,j);
		}
	sort(a,a+cnt,cmp);
	for(int i=1,r;i<cnt;i=r)
	{
		r=i;
		while(r<cnt && a[i].w==a[r].w)     //相同边权
			r++;
		for(int j=i;j<r;++j)
            g[a[j].l]=f[a[j].l],g[a[j].r]=f[a[j].r];
		FOR(j,i,r-1)                      //共同处理
		{
			int u=a[j].l;
			int v=a[j].r;
			g[u]=max(g[u],f[v]+1);
			if(!u)       // 如果是原点的话不用多计算
				continue;
			swap(u,v);             //反向建边
			g[u]=max(g[u],f[v]+1);
		}
		for(int j=i;j<r;++j)
            f[a[j].l]=g[a[j].l],f[a[j].r]=g[a[j].r];
	}
	cout<<f[0]-1;               //由于记录是从0开始的所以我们计算所得结果-1
	return 0;
}

你可能感兴趣的:(比赛题解,牛客,算法)