[NOI2010] 成长快乐 题解

初步分析

nemo 肯定走直线

​ 不妨把 nemo 想象成只能以速度 vv 运动或静止。而在最优策略中,nemo 显然不会静止不动。

​ 所以 nemo 的最优策略一定取如下形态:以某种顺序把食物排成一列 {pi}\{p_i\},从 {p1}\{p_1\} 开始一个一个吃,每次都走当前最短时间对应路径

​ 因为 nemo 已知了各食物的位置和速度,nemo 可以在目标食物的路径上某点等待,而最优的该点必然满足 nemo 和食物同时到达。因而可以计算出其到单个食物所需的最短时间。

​ 简单的高中解三角形问题,这里不再赘述。设 nemo 与食物距离为 ss,食物运动的方向与 nemo 相对于食物的方向夹角为 θ\theta,nemo 速度为 vv,食物速度为 uu,联合运用向量点积和余弦定理得出 nemo 走到食物的最短时间为

t=sucosθ±u2(cos2θ1)+v2u2v2t=s\frac{u\cos\theta\pm\sqrt{u^2(\cos^2\theta-1)+v^2}}{u^2-v^2}

​ 等式成立性与加减号取法:

  • Δ=u2(cos2θ1)+v2<0\Delta=u^2(\cos^2\theta-1)+v^2<0cosθ<0&&u>v\cos\theta<0\&\& u>v 时,nemo 追不上。
  • 否则取减号一定更优并且 tt 必为正。证明就是用 Δ0\Delta\ge 0 化一下。
  • 特别地,u=vu=v 特判掉。

​ 目标变为求最优的排列 {pi}\{p_i\}

点一

​ 就两个点手玩都可以啊喂

点二

​ 枚举全部 8!=403208!=40320 种排列。

点三

​ 特点:nemo 一开始体重太小而食物权值呈 22 的整次幂。

​ 只能从小往大吃。

点四

​ 特点:所有食物与 nemo 共线且静止不动。所有食物权值不过 200。

​ 先慢慢增大自己的体重,达到一定量可以吃所有食物后进行区间 DP 即可。

点五,六

​ 特点:所有食物在高空以高速下落,水平位置相隔近。nemo 速度小,但可以吃所有食物。

​ 可以看做 nemo 只能在横轴上运动,接住下落的食物。

​ 设 fif_i 表示接住第 ii 个食物所能获得的最大体重,直接转移。

​ 点六有一个坑在于有两个食物会同时下落到相同位置。

点七,八

​ 特点:如图。数字三角形的形态已经很明显了。观察权值发现每一行只能选一个,而时间恰好够 nemo 走到最后一排。

​ 数字三角形即可。

点九

​ 特点:所有食物静止

​ 跟点十一起做。

点十

​ 特点:好像没有

​ 这道题唯一折磨人的点。

​ 考虑一个贪心策略:每次在当前所有能吃的食物中按某种关系选出最优的那一个并吃掉。重复该流程直至找不到可以吃的食物为止。

​ 设对于一个食物 ii,其权值为 wiw_i,nemo 吃掉它所需时间为 tit_i,nemo 吃掉它时 nemo 的位置距当前位置为 did_i

​ 肯定会有一些感性的认识:wiw_i 越大越好,tit_i 越小越好,did_i 越小越好,witi\frac{w_i}{t_i} 越大越好 \cdots

​ 各人有各人的偏好,各种调参方法都能过,但调参的过程无一例外的折磨。这里题解采用

ki=witi2+1K1di+K2k_i=\frac{w_i}{t_i^2}+\frac{1}{K_1d_i}+K_2

​ 每次选 kik_i 最大的吃。K1,K2K_1,K_2 为两个 [1,10][1,10] 的随机数。

多次贪心取最优解。实测在 10 次之内可以出答案,所花时间不超过 5s。

double eat2(){
	while(!pq.empty())pq.pop();
	double ct,nans=w0,nt=0;nx=fx,ny=fy,pa=0;
	for(int i=1;i<=n;i++){
		fl[i]=0;
		if(w[i]<nans){
			ct=calnt(i,nt);
			pq.push(make_pair(w[i]/(ct*ct)+G/dis(nx,ny,ax[i]+nt*mx[i],ay[i]+nt*my[i])+rand()%10,i));
		}
	}
	while(!pq.empty()){
		int ng=pq.top().second;pq.pop();
		fl[ng]=1,ct=calnt(ng,nt);
		if(nt+ct>T)break;
		nx=ax[ng]+(nt+ct)*mx[ng];
		ny=ay[ng]+(nt+ct)*my[ng];
		na[++pa]=(eve){nt+ct,nx,ny,ng};
		nans+=w[ng];
		nt+=ct;
		while(!pq.empty())pq.pop();
		for(int i=1;i<=n;i++)if(w[i]<nans&&!fl[i])ct=calnt(i,nt),pq.push(make_pair(w[i]/(ct*ct)+G/dis(nx,ny,ax[i]+nt*mx[i],ay[i]+nt*my[i])+rand()%10,i));
	}
	if(nans>aans){
		aans=nans,pans=pa;
		for(int i=1;i<=pa;i++)ans[i]=na[i];
	}
	return nans;
}