2021牛客暑期多校训练营8 K题: Yet Another Problem About Pi

本文解析了一个关于计算在二维空间中,以π公里路径遍历分割区域数量的问题,涉及最优路径策略和非负整数方程求解。重点在于比较走短边与对角线策略,以及如何根据区域成本比率调整操作方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

K题: Yet Another Problem About Pi

原题链接:https://ac.nowcoder.com/acm/contest/11259/K

题目大意

在二维平面上,在x轴方向上每 www km画一条竖线,在y轴方向上每 ddd km画一条横线,这些线将平面分割为若干个 w×dw\times dw×d 大小的矩形区域。
求从任意点开始,以任意形式的路径行驶 π\piπ km,最多可以访问到多少区域。

题解

首先考虑一下访问的出发点,若我们从一个点出发,则我们可以移动微小的距离(小到不影响精度范围内,而且因为 π\piπ 是无理数,可以理解为其极后的一个数位发生了变动,可以不计)来直接访问周围的 444 个区域,显然,这是最优的。
两点之间线段最短,因此我们尽量考虑走直线,首先显而易见的,我们移动 min⁡(w,d)\min(w,d)min(w,d) 的距离(即较短边)来达到一个相邻的点,然后微动访问 222 个新区域,如下图(蓝色起始,绿色为新增,红色为路径):
在这里插入图片描述
但还有一种方案,我们走单个区域的对角线( w2+d2\sqrt{w^2+d^2}w2+d2 )来到达一个相邻的点,不难发现,此时微动访问的新区域数量变为了 333 个,如下图(蓝色起始,绿色为新增,红色为路径):
在这里插入图片描述
显然,最终区域数与两种方案的执行顺序无关(平面无限大),只与两种方案的执行数量有关。
我们设 a=min⁡(w,d),b=w2+d2a=\min(w,d),b=\sqrt{w^2+d^2}a=min(w,d),b=w2+d2 ,则问题转化为求满足 ax+by≤πax+by\le \piax+byπ 的非负整数 x,yx,yx,y 代入 2x+3y2x+3y2x+3y 中得到的最大值。
显然,我们可以考虑优先采取区域/代价比 a2\frac{a}{2}2ab3\frac{b}{3}3b 更优者来接近最优解:

  1. a2<b3(3a<2b)\frac{a}{2}<\frac{b}{3}(3a<2b)2a<3b(3a<2b) 则可以采用 2y(6区域)⟶3x(6区域)2y(6区域)\longrightarrow 3x(6区域)2y(6)3x(6) 的方式在不减少区域的前提下减少花费(可能节省以得到更多操作次数);
  2. b3<a2(2b<3a)\frac{b}{3}<\frac{a}{2}(2b<3a)3b<2a(2b<3a) 则可以采用 3x(6区域)⟶2y(6区域)3x(6区域)\longrightarrow 2y(6区域)3x(6)2y(6) 的方式在不减少区域的前提下减少花费(可能节省以得到更多操作次数);
  3. a2=b3(3a=2b)\frac{a}{2}=\frac{b}{3}(3a=2b)2a=3b(3a=2b) 则任选(实际操作优先选择增加 xxx ( aaa 操作次数)会更优,因为单步花费少更容易细分利用路径长度);

根据以上转换规则,不难发现在最终完全单向转化后的非负整数 x,yx,yx,y 中, x<3x<3x<3y<2y<2y<2 至少成立一条,则我们枚举其中一者的数量为 000~222 ,然后计算最大化利用路径得到的解即可。

补: 关于为什么解可能不是单种操作

只使用单种操作,最终结果可能会有较大浪费(剩余长度不足以执行任何一种长度),此时若减少原优先操作,改为另一种实现更优的路径长度利用,则可能得到更优解。
y<2(优先x)y<2(优先x)y<2(x) 时可能发生的转化来源:
⌊x⌋=1(区域数2)⟶⌊y⌋=1(区域数3)\lfloor x\rfloor =1(区域数2)\longrightarrow \lfloor y\rfloor =1(区域数3)x=1(2)y=1(3)

x<3(优先y)x<3(优先y)x<3(y) 时可能发生的转化来源:
⌊y⌋=1(区域数3)⟶⌊x⌋=2(区域数4)\lfloor y\rfloor =1(区域数3)\longrightarrow \lfloor x\rfloor =2(区域数4)y=1(3)x=2(4)

参考代码

#include<bits/stdc++.h>
#define ll long long
#define For(i,n,m) for(int i=n;i<=m;i++)
#define FOR(i,n,m) for(int i=n;i>=m;i--)
using namespace std;
void read(int &x){
   int ret=0;
   char c=getchar(),last=' ';
   while(!isdigit(c))last=c,c=getchar();
   while(isdigit(c))ret=ret*10+c-'0',c=getchar();
   x=last=='-'?-ret:ret;
}

const double p=acos(-1);//通过acos(-1)获得pi的值
int T,ans;
double w,d,fi;

int main()
{
   read(T);
   while(T--){
   	scanf(" %lf %lf",&w,&d);
   	ans=0;
   	double fi=min(w,d),se=sqrt(w*w+d*d);//预处理
   	For(i,0,2){//考虑单种0~2
   		if(p-i*fi>0)ans=max(ans,(int)((p-i*fi)/se)*3+2*i);//i为走短边次数,注意保证此时走对角线次数非负
   		if(p-i*se>0)ans=max(ans,(int)((p-i*se)/fi)*2+3*i);//i为走对角线次数,注意保证此时走短边次数非负
   	}
   	printf("%d\n",ans+4);
   }
   return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值