[UESTC T3] 蛋糕

题目描述

今天是鲍勃的生日,爱丽丝打算做一个蛋糕送给他。

这是鲍勃的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

我们可以得到如下等式:

JQF=2×π/n ∠ J Q F = 2 × π / n

JQ=JF2sinJQF2 J Q = J F 2 s i n ∠ J Q F 2

Q1Q=cos(JQK2)×JQ Q 1 Q = c o s ( ∠ J Q K 2 ) × J Q

NQQ1=NQM2=2×πm ∠ N Q Q 1 = ∠ N Q M 2 = 2 × π m

NM=2×NQ1=2×tan(NQQ1)×Q1Q N M = 2 × N Q 1 = 2 × t a n ( ∠ N Q Q 1 ) × Q 1 Q

所以我们就可以高兴地算出外部多边形的边长啦…

代码如下:

#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;  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值