GYM 100182 G.Pool Table(枚举)

本文探讨了一种通过几何变换解决台球桌上白球碰撞指定次数后抵达黑球位置的问题。利用平面翻折原理,将碰撞路径转换为直线,通过枚举目标位置并排除提前碰撞的情况来寻找最短路径。

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

Description
给出一个台球桌的长和宽,以及黑球和白球的坐标,问白球在恰经过n次碰撞碰到黑球的最短路径长度是多少
Input
多组用例,每组用例输入两整数l和w分别表示台球桌的长和宽,然后输入白球和黑球的横纵坐标cx,cy,tx,ty,最后输入n
(2<=l,w<=100,0 < cx,tx < l,0 < cy,ty < w,0<=n<=100)
Output
输出白球恰n次碰到黑球的最短路径长度
Sample Input
20 15 10 1 12 1 1
10 20 1 2 7 16 2
0 0 0 0 0 0 0
Sample Output
2.828
19.698
Solution
碰撞路径的折线可以通过对矩形的翻折变成直线,所以我们可以把整个平面分成若干l*w的矩形,两个矩形有公共边表示白球可以通过碰撞这条公共边达到在矩阵之间运动的目的,我们把每个矩形中的黑球坐标求出来,这样就知道了白球的可能目的地,也就是外围一圈的黑球,这一圈的矩形到原矩形都需要碰n次,但是这些目的地可能有不合法的,也就是说白球在中间位置就碰到了黑球,故我们还需判断这个斜率(即白球到黑球的斜率)在那些碰撞次数小于n的情况中是否出现过,所以可以先枚举那些碰撞次数小于n的,把斜率的最简形式用map存起来,然后再去枚举碰次数等于n的,如果这个斜率出现过则不合法,否则更新答案
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 1e12
typedef pair<int,int>P;
map<P,int>M;
int l,w,cx,cy,tx,ty,n;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
P deal(int x,int y)
{
    if(x==0)return P(0,y/abs(y));
    if(y==0)return P(x/abs(x),0);
    int g=gcd(abs(x),abs(y));
    return P(x/g,y/g);
}
int main()
{
    while(~scanf("%d%d%d%d%d%d%d",&l,&w,&cx,&cy,&tx,&ty,&n),l||w||cx||cy||tx||ty||n)
    {
        M.clear();
        for(int i=-n;i<=n;i++)
            for(int j=-n;j<=n;j++)
                if(abs(i)+abs(j)<n)
                {
                    int x,y;
                    if(abs(i)%2)x=i*l+l-tx;
                    else x=i*l+tx;
                    if((abs(j))%2)y=j*w+w-ty;
                    else y=j*w+ty;
                    x-=cx,y-=cy;
                    M[deal(x,y)]++; 
                }
        double ans=INF;
        for(int i=-n;i<=n;i++)
            for(int j=-n;j<=n;j++)
                if(abs(i)+abs(j)==n)
                {
                    int x,y;
                    if(abs(i)%2)x=i*l+l-tx;
                    else x=i*l+tx;
                    if(abs(j)%2)y=j*w+w-ty;
                    else y=j*w+ty;
                    x-=cx,y-=cy;
                    if(M.find(deal(x,y))==M.end())
                    {
                        double temp=sqrt(1.0*x*x+1.0*y*y);
                        ans=min(ans,temp);
                    }
                }
        printf("%.3f\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值