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;
}