[BZOJ4431][Nwerc2015]Hole in One一杆进洞

[Nwerc2015]Hole in One一杆进洞

Description
Janine recently went to her local game store and bought “Hole in One”, a new mini-golf game for her computer. As indicated by the name, the objective of the game is to shoot a ball into a hole using just one shot. The game also borrows elements from brick breaker style games: in the playing field, several walls are placed that will be destroyed upon being hit by the ball. The score of a successful shot depends on the number of destroyed walls, so Janine wonders: what is the maximum number of walls that can be hit while performing a “Hole in One”?
For the purposes of this problem you can think of the playing field as a cartesian plane with the initial position of the ball at the origin. The walls are non-intersecting axis-parallel line segments in this plane (i.e., parallel to either the x axis or the y axis). The diameter of the ball is negligible so it is represented as a single point.
Figure 1: Illustration of the first sample input: The ball first bounces off two walls at points 1 and 2. When it passes point 3 the wall has already vanished.
Whenever the ball hits a wall, two things happen:
The direction of the ball changes in the usual way: the angle of incidence equals the angle of reflection.
The wall that the ball touched is destroyed. Following common video game logic, no rubble of the wall remains; it will be as though it vanished.
The behaviour of the ball is also affected by the power of the shot. In particular, an optimal shot may need to first roll over the hole, then hit some more walls, and only later drop into the hole.
The input consists of:
one line with one integer n (0≤n≤8), the number of walls;
one line with two integers x and y, the coordinates of the hole;
nn lines each with four integers x1, y1, x2, and y2 (either x1=x2, or y1=y2, but not both), representing a wall with end points (x1,y1) and (x2,y2).
The hole is not at the origin and not on a wall. The walls do not touch or intersect each other. No wall lies completely on the x axis or the y axis. All coordinates in the input are integers with absolute value at most 1000.
If there is no way to shoot the ball such that it reaches the hole, print “impossible”. Otherwise, print the maximum number of walls that can be destroyed in a single “Hole in One” shot.
珍妮最近去当地的游戏商店买了“一杆进洞”,一个新的迷你高尔夫电脑游戏。正如名称所示,这个游戏的目标是,将球只用一击击进洞里。游戏还借鉴了打砖块风格的游戏元素:在场上,放置了一些墙,它们被球击中就会被摧毁。一个成功的击球的得分取决于破坏墙的数量,所以珍妮不禁要问:在一次“一杆进洞”中能够摧毁的最多的墙数量是多少?
对于这个问题的意向,你可以认为,比赛场地为笛卡尔坐标平面,球的最初位置在原点。墙壁为在该平面中互不相交且与坐标轴平行的线段(即平行于x轴或y轴)。球的直径非常小以至于把它当成一个点。
图1:第一个样例输入的插图:球首先经过1,2号点,并在3号点时穿过了被摧毁的墙。
每当球碰到一堵墙,会发生:
球的方向以通常的方式改变:入射角等于反射角。
被球触碰的墙被破坏。 游戏逻辑规定,没有墙的废墟存在,认为它消失了。
球的行为也受到击球的力影响。特别的是,最佳的击球可能需要将鼠标放在洞上,然后摧毁一些墙壁,并且只是延迟入洞。
Input
一行一个整数n(0≤n≤8),墙壁的数量。
一行两个整数x和y,洞的坐标。
n行,每行各有四个整数x1,y1,x2与y2(x1 = x2或y1 = y2,但不会满足两者),表示墙壁的两个端点。
洞不会在原点也不会在墙上。墙壁不会互相接触或交叉。没有墙会完全在x轴或y轴上。输入的所有的坐标都是整数且绝对值最大1000。
Output
如果没有办法一击入洞,输出“impossible”。否则,输出在一次“一杆进洞”中能够摧毁的最多的墙的数量。
Sample Input
Sample Input 1
3
4 2
1 1 1 2
2 1 2 2
3 1 3 2
Sample Input 2
1
2 0
1 -1 1 1
Sample Input 3
2
-2 4
2 4 2 2
0 6 -2 6
Sample Output
Sample Output 1
2
Sample Output 2
Impossible
Sample Output 3
2

Solution
首先从大到小枚举答案,再答案!枚举方案,通过镜面对称终点得到路线,然后平方验证路线
注意细节

Code

#include <bits/stdc++.h>
using namespace std;
#define MS(_) memset(_,0,sizeof(_))
template<typename T> inline void read(T &x){
    x=0; T f=1; char ch=getchar();
    while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar(); }
    while (isdigit(ch))  {x=x*10+ch-'0'; ch=getchar(); }
    x*=f;
}

const int INF=0x7fffffff;
const int MaxN=100;
const double eps=0;
struct Vec{
    double x,y;
    Vec(){}
    Vec(double _x,double _y):x(_x),y(_y){}
}p1[MaxN],p2[MaxN],T,p[MaxN];
inline Vec operator + (const Vec &a,const Vec &b){return Vec(a.x+b.x,a.y+b.y);}
inline Vec operator - (const Vec &a,const Vec &b){return Vec(a.x-b.x,a.y-b.y);}
template<typename T>inline Vec operator * (const Vec &a,T b){return Vec(a.x*b,a.y*b);}
template<typename T>inline Vec operator * (T &a,const Vec &b){return Vec(a*b.x,a*b.y);}
template<typename T>inline Vec operator / (const Vec &a,T b){return Vec(a.x/b,a.y/b);}
inline double cross(const Vec &a,const Vec &b){return a.x*b.y-b.x*a.y;}
inline double dot(const Vec &a,const Vec &b){return a.x*b.x+a.y*b.y;}
typedef Vec Poi;
int n,N,type[MaxN],used[MaxN],vis[MaxN],lst[MaxN],ans;

inline Poi calnxt(const Vec &a,int no){return type[no]?Vec(a.x,p1[no].y*2-a.y):Vec(p1[no].x*2-a.x,a.y);}
inline Vec caldot(Poi a,Poi b,Poi c,Poi d){
    if (a.x==b.x){
        if (c.x==d.x) return Vec(-INF,-INF);
        if (c.y==d.y) {swap(a,c);swap(b,d);}
    }    
    double k=(b.y-a.y)/(b.x-a.x),dt=b.y-k*b.x;
    if (c.x==d.x) return Vec(c.x,k*c.x+dt);
    else return Vec((c.y-dt)/k,c.y);
}
inline bool shot(){
    Poi now=Vec(0.,0.); MS(used);
    for (int i=N;i;i--){
        Poi nxt=p[i],wall1=p1[lst[i]],wall2=p2[lst[i]];
        Poi point=caldot(now,nxt,wall1,wall2);
        if(dot(point-wall1,point-wall2)>0||dot(point-now,point-nxt)>0) return false;
        used[lst[i]]=1;
        for (int j=1;j<=n;j++) if (!used[j]){
            Poi point2=caldot(now,point,p1[j],p2[j]);
            if(dot(point2-p1[j],point2-p2[j])<=0&&dot(point2-now,point2-point)<=0) return false;
        }
        now=point;
    }
    Poi nxt=p[0];
    for (int j=1;j<=n;j++) if (!used[j]){
        Poi point2=caldot(now,nxt,p1[j],p2[j]);
        if(dot(point2-p1[j],point2-p2[j])<=0&&dot(point2-now,point2-nxt)<=0) return false;
    }
    return true;
}

inline void check(){
    p[0]=T;
    for (int i=1;i<=N;i++)p[i]=calnxt(p[i-1],lst[i]);
    if (shot()) ans=N;  
}
void dfs(int dep){
    if (~ans) return;
    if (dep>N) {check();return;}
    for(int i=1;i<=n;i++){
        if (!vis[i]){vis[lst[dep]=i]=1; dfs(dep+1); vis[i]=0;}
        if (~ans) return;
    }   
}
int main(){
    read(n);
    read(T.x);read(T.y);
    for(int i=1;i<=n;i++){read(p1[i].x);read(p1[i].y);read(p2[i].x);read(p2[i].y);}
    for(int i=1;i<=n;i++) 
        if (p1[i].x==p2[i].x){type[i]=0; if (p1[i].y>p2[i].y) swap(p1[i],p2[i]);}
        else{type[i]=1; if (p1[i].x>p2[i].x) swap(p1[i],p2[i]);}

    ans=-1;
    for(int i=n;i>=0;i--){
        N=i; dfs(1); 
        if (~ans) break;
    }
    if (!~ans) puts("impossible"); else printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(计算几何)