题目描述
今天是鲍勃的生日,爱丽丝打算做一个蛋糕送给他。
这是鲍勃的n岁生日,所以爱丽丝的蛋糕必须是n边形。而且,鲍勃很喜欢数字m,所以这个蛋糕必须放在一个m边形的盒子里。为让气氛更加浪漫, 爱丽丝将在蛋糕的中心插上一根蜡烛,显然,蜡烛既在蛋糕的中心,又在盒子的中心是最好的。
换句活说,爱丽丝应该使边形的蛋糕能被容m边形的盒子里,且 使其中心重合。事实上,爱丽丝己经做好蛋糕,蛋糕是边长力1的n边形,现在她想知道:m边形盒子的最小边长是多少。
输入格式
每组测试数据也包含多行数据,以EOF作为文件结束,每行包括两个正整数n和m。
输出格式
输出包含多行,每行包含一个整数,代表最小的符合条件的m边形盒子的边长,保留4位小数。
输入样例1
4 8
输出样例1
0.5412
输入样例2
8 4
输出样例2
2.4142
数据范围
对于20%的数据,
n,m<100
n
,
m
<
100
;
对于40%的数据,
n,m<10000
n
,
m
<
10000
;
对于60%的数据,
n,m<1000000
n
,
m
<
1000000
;
对于80%的数据,
n,m<100000000
n
,
m
<
100000000
;
对于100%的数据,
n,m<1000000000
n
,
m
<
1000000000
。
解题分析
作为蒟蒻的我在考场上当然没有做出这道题QAQ, 在THU神犇wuvin的讲解后得知了可以使用随机算法得到近似最优解后就试了一下。 我们可以假设外侧的m边形正放不动, 不断随机旋转内部n边形, 通过检测抵死每个角时外侧m边形的边长来得到最优解。
PS:在n,m差距悬殊的时候误差会相当大, 如图:
10次计算没有一次相同……但将旋转次数开大后又会TLE。
但是却玄学连续AC了…如图:
只能说明出题人用心出题,用脚造数据。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <ctime>
#include <iostream>
using namespace std;
#define db double
//double在这里精度已够(只需要4位精度),long double 不仅精度没有明显改观
//反而还会T掉(运算太慢)
#define R register
#define gc getchar()
#define IN inline
#define W while
#define ll long long
#define EPS 1e-8
const db PI = acos ((db) -1);
db o_angle, i_angle, base, half_cross, half_o_angle, half_i_angle;
int out, in;
template <class T>
IN T abss (const T &x)//自带的abs会出现妙妙的情况
{return x > 0 ? x : -x;}
IN db get_long_double()
{//随机double生成器
int base = 0;
ll decimal = 0LL;
base = rand();
decimal = abss(rand() * rand() - rand() + rand() * rand());
db rt = base + (db)decimal / (db)10000000.0L;
return rt;
}
IN db solve()
{
db avai = 9999999999.0L, gt, cha, dia, edge, ans;
int tim = 20000;//暴力旋转中间的多边形20000次(12500次足够了)(~~可能不够~~)
W (tim--)
{
ans = 0.0L;
gt = get_long_double();
gt = gt - ((ll)(gt / PI) * PI);
int cnt = 100;
W (cnt--)
{
//每次加中间多边形的一个心角
gt = gt - ((ll)(gt / o_angle)) * o_angle;
cha = abss(gt - half_o_angle);
dia = cos(cha) * half_cross;
edge = tan(half_o_angle) * dia;
if(edge > ans) ans = edge;
gt += i_angle;//用其他内角检验是否可以卡入外侧多边形
}
if(avai > ans) avai = ans;
}
return avai;//返回最优解
}
int main()
{
srand(time(0));
W (scanf("%d%d", &in, &out) != EOF)
{
o_angle = 2.0 * PI / out; //outer angle
i_angle = 2.0 * PI / in; //inner angle
half_o_angle = o_angle / 2.0L;
half_i_angle = i_angle / 2.0L;
half_cross = 0.5L / sin(half_i_angle); //inner polygon's half diagonal
printf("%.4lf\n", solve() * 2.0);
}
return 0;
}
现在我们来谈谈正解,可以发现当n % m == 0的时候,所有内多边形内部的的顶点都在外多边形的顶点上;当m % n == 0的时候, 所有与外多边形相交的边都与外多边形的边重合。为了方便计算, 我们可以将 lcmn,m l c m n , m 作为一个中介, 进行计算。
考虑下图中 △BFJ △ B F J 和 □MNOP ◻ M N O P
我们可以得到如下等式:
所以我们就可以高兴地算出外部多边形的边长啦…
代码如下:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
#define R register
#define gc getchar()
#define IN inline
#define W while
#define db double
#define ll long long
const db PI = acos((db)-1);
db i_angle, o_angle, rad, c_angle, seg1, seg2, line, angle;
int main()
{
ll in, out, cir, num;
W (scanf("%lld%lld", &in, &out) != EOF)
{
i_angle = 2.0 * PI / in;
cir = in * out / __gcd(in, out);
c_angle = 2.0 * PI / cir;
rad = 0.5 / sin(i_angle / 2.0);
line = (rad * cos(c_angle / 2.0));
seg2 = line * tan(PI / out);
printf("%.4lf\n", 2.0 * seg2);
}
return 0;
}