K题: Yet Another Problem About Pi
原题链接:https://ac.nowcoder.com/acm/contest/11259/K
题目大意
在二维平面上,在x轴方向上每
w
w
w km画一条竖线,在y轴方向上每
d
d
d km画一条横线,这些线将平面分割为若干个
w
×
d
w\times d
w×d 大小的矩形区域。
求从任意点开始,以任意形式的路径行驶
π
\pi
π km,最多可以访问到多少区域。
题解
首先考虑一下访问的出发点,若我们从一个点出发,则我们可以移动微小的距离(小到不影响精度范围内,而且因为
π
\pi
π 是无理数,可以理解为其极后的一个数位发生了变动,可以不计)来直接访问周围的
4
4
4 个区域,显然,这是最优的。
两点之间线段最短,因此我们尽量考虑走直线,首先显而易见的,我们移动
min
(
w
,
d
)
\min(w,d)
min(w,d) 的距离(即较短边)来达到一个相邻的点,然后微动访问
2
2
2 个新区域,如下图(蓝色起始,绿色为新增,红色为路径):
但还有一种方案,我们走单个区域的对角线(
w
2
+
d
2
\sqrt{w^2+d^2}
w2+d2 )来到达一个相邻的点,不难发现,此时微动访问的新区域数量变为了
3
3
3 个,如下图(蓝色起始,绿色为新增,红色为路径):
显然,最终区域数与两种方案的执行顺序无关(平面无限大),只与两种方案的执行数量有关。
我们设
a
=
min
(
w
,
d
)
,
b
=
w
2
+
d
2
a=\min(w,d),b=\sqrt{w^2+d^2}
a=min(w,d),b=w2+d2 ,则问题转化为求满足
a
x
+
b
y
≤
π
ax+by\le \pi
ax+by≤π 的非负整数
x
,
y
x,y
x,y 代入
2
x
+
3
y
2x+3y
2x+3y 中得到的最大值。
显然,我们可以考虑优先采取区域/代价比
a
2
\frac{a}{2}
2a 与
b
3
\frac{b}{3}
3b 更优者来接近最优解:
- 若 a 2 < b 3 ( 3 a < 2 b ) \frac{a}{2}<\frac{b}{3}(3a<2b) 2a<3b(3a<2b) 则可以采用 2 y ( 6 区 域 ) ⟶ 3 x ( 6 区 域 ) 2y(6区域)\longrightarrow 3x(6区域) 2y(6区域)⟶3x(6区域) 的方式在不减少区域的前提下减少花费(可能节省以得到更多操作次数);
- 若 b 3 < a 2 ( 2 b < 3 a ) \frac{b}{3}<\frac{a}{2}(2b<3a) 3b<2a(2b<3a) 则可以采用 3 x ( 6 区 域 ) ⟶ 2 y ( 6 区 域 ) 3x(6区域)\longrightarrow 2y(6区域) 3x(6区域)⟶2y(6区域) 的方式在不减少区域的前提下减少花费(可能节省以得到更多操作次数);
- 若 a 2 = b 3 ( 3 a = 2 b ) \frac{a}{2}=\frac{b}{3}(3a=2b) 2a=3b(3a=2b) 则任选(实际操作优先选择增加 x x x ( a a a 操作次数)会更优,因为单步花费少更容易细分利用路径长度);
根据以上转换规则,不难发现在最终完全单向转化后的非负整数 x , y x,y x,y 中, x < 3 x<3 x<3 和 y < 2 y<2 y<2 至少成立一条,则我们枚举其中一者的数量为 0 0 0~ 2 2 2 ,然后计算最大化利用路径得到的解即可。
补: 关于为什么解可能不是单种操作
只使用单种操作,最终结果可能会有较大浪费(剩余长度不足以执行任何一种长度),此时若减少原优先操作,改为另一种实现更优的路径长度利用,则可能得到更优解。
当
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;
}