首页 > ACM题库 > HDU-杭电 > HDU 3263-Ancient vending machine-计算几何-[解题报告]HOJ
2014
03-13

HDU 3263-Ancient vending machine-计算几何-[解题报告]HOJ

Ancient vending machine

问题描述 :

Though the story of Shaolong Xiang, the great time-traveler, is not recorded in the history, some recent discoveries show the possibility that the ancient mystery visitor might really exist. Several months ago, a strange machine was found together with some newly unearthed terra-cotta warriors. That machine looks like a modern machine and even has some electronic parts ― that’s why people take Mr. Xiang into consideration when guessing the origin of the strange machine.

After careful examinations, scientists finally figured out what the machine is — it actually is a vending machine in Qin dynasty! There is a hole on a panel of the machine, and scientists are pretty sure that Qin people put their coins into the machine through that hole to buy things. As you know, there were too many different types of coins before Qin dynasty and Emperor Qin Shihuang didn’t like that. But scientists found an announcement made by Qin Shihuang carved on the machine, saying that any coins which can go through the hole is still legal to use. Now your task is to determine whether a certain type of coin can be put into the hole―from these results we might find some clues about the mystery time-traveler.

The hole on the panel is a polygon and all coins also have a shape of polygon. THEY ALL MAYBE CONCAVE. Scientist found out that ancient people use the vending machine this way: The panel with the hole was positioned horizontally, and the customer chose a best position above the hole to hold a coin. Then the customer dropped the coin. During the process of felling down, the coin WOULD NOT ROTATE in any direction. If the coin could go through the hole, the coin is considered “legal”. Otherwise, the coin is judged “illegal”. If the coin just merely touched the edges of the hole but was not blocked, it was considered as legal. For example, as shown in the sample input, a square coin with side length 5 can go through a triangle hole with sides of length 3, 4 and 5 , so it’s legal.

Seat taking up is tough

Please note that coins and the panel are all considered as no thickness.

输入:

The first line contains an integer T representing the number of test cases( 0 < T <= 20).
For each test case, the first line contains an integer N (3<=N<=20), representing the number of vertexes of the hole-polygon.
Next N lines describe the shape of the hole-polygon by listing positions of all vertexes in counterclockwise order. Each line contains two real numbers r1 and r2 ( -100 <= r1,r2 <=100), describing the position of a vertex.

The next line contains an integer M (3<=M<=20), representing the number of vertexes of the coin-polygon.

Next M lines describe the shape of the coin-polygon by listing positions of all vertexes in counterclockwise order. Each line contains two real numbers r1 and r2 ( -100 <= r1,r2 <=100), describing the position of a vertex.

The positions mentioned above are relative coordinates but not absolute values.

输出:

The first line contains an integer T representing the number of test cases( 0 < T <= 20).
For each test case, the first line contains an integer N (3<=N<=20), representing the number of vertexes of the hole-polygon.
Next N lines describe the shape of the hole-polygon by listing positions of all vertexes in counterclockwise order. Each line contains two real numbers r1 and r2 ( -100 <= r1,r2 <=100), describing the position of a vertex.

The next line contains an integer M (3<=M<=20), representing the number of vertexes of the coin-polygon.

Next M lines describe the shape of the coin-polygon by listing positions of all vertexes in counterclockwise order. Each line contains two real numbers r1 and r2 ( -100 <= r1,r2 <=100), describing the position of a vertex.

The positions mentioned above are relative coordinates but not absolute values.

样例输入:

2
4
0 0
4 0
4 1
0 1
3
0 4
1 4
1 0
3
0 0
4 0
0 3
4
0 0
5 0
5 5
0 5

样例输出:

legal
legal

题目:给定一个多边形A,问能否穿过多边形的洞B。

分析:计算几何、凸包、旋转卡壳、点与多边形关系。问题实际是在求多边形A的最小宽度和多边形B能容纳的最常线段长度。

                                                   Ancient vending machine                                                   

1.对于多边形A,构造凸包利用,旋转卡壳求出对踵点对,然后求出最小的高。

   

                                 Ancient vending machine

2. 对于多边形B,所求的最长线段,一定经过多边形上的至少两个点,枚举所有点对,计算被截取的最长部分即可。

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>

using namespace std;

typedef struct pnode
{
    double x,y,d;
    pnode( double a, double b ) {x = a;y = b;}
    pnode(){};
}point;
point H[ 21 ];
point C[ 21 ];
point P0,Pn;

typedef struct lnode
{
    double x,y,dx,dy,d;
    int    id,hit,sign;
    lnode( point a, point b ) {x = a.x;y = a.y;dx = b.x-a.x;dy = b.y-a.y;}
    lnode(){};
}line;

//两点间距离 
double dist( point a, point b )
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

//点到直线距离 
double dist( point a, line l )
{
    return fabs(l.dx*(a.y-l.y)-l.dy*(a.x-l.x))/sqrt(l.dx*l.dx+l.dy*l.dy);
}

//叉乘 ab*ac 
double crossproduct( point a, point b, point c )
{
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

//坐标排序 
bool cmp1( point a, point b )
{
    if ( a.x == b.x ) return a.y < b.y;
    else return a.x < b.x;
}

//级角排序 
bool cmp2( point a, point b )
{
    double cp = crossproduct( P0, a, b );
    if ( cp == 0 ) return a.d < b.d;
    else return cp > 0;
}

//凸包扫描算法 
double Graham( int N )
{
	sort( C+0, C+N, cmp1 );
	P0 = C[0];
    for ( int i = 1 ; i < N ; ++ i )
        C[i].d = dist( P0, C[i] );
    sort( C+1, C+N, cmp2 );
        
    //计算凸包 
    int top = 2;
    for ( int i = 3 ; i < N ; ++ i ) {
        while ( top > 0 && crossproduct( C[top-1], C[top], C[i] ) <= 0 ) -- top;
        C[++ top] = C[i];
    }
	C[++ top] = C[0];
    
    //旋转卡壳,求对踵点对 
   	int    L = 0,R = 1;
   	double D = 500.000;
   	do{
		while ( crossproduct( C[R], C[L], C[(L+1)%top] ) <= crossproduct( C[(R+1)%top], C[L], C[(L+1)%top] ) )
			R = (R+1)%top;
		
		D = min( D, dist( C[R], line( C[L], C[(L+1)%top] ) ) );
		L = (L+1)%top;
	}while ( L );
	
	return D;
}

//直线与线段相交判断 
bool l_cross_s( line b, line a )
{
    double t1 = b.dx*(a.y-b.y)-b.dy*(a.x-b.x);
    double t2 = b.dx*(a.y+a.dy-b.y)-b.dy*(a.x+a.dx-b.x);
    return t1*t2 < 0;
}

//线段相交   
bool s_cross_s( line a, line b )  
{  
	double t1 = 0.0+a.dx*(b.y-a.y)-a.dy*(b.x-a.x);  
	double t2 = 0.0+a.dx*(b.y+b.dy-a.y)-a.dy*(b.x+b.dx-a.x);  
	double t3 = 0.0+b.dx*(a.y-b.y)-b.dy*(a.x-b.x);  
	double t4 = 0.0+b.dx*(a.y+a.dy-b.y)-b.dy*(a.x+a.dx-b.x);  
	return (t1*t2 < 0)&&(t3*t4 < 0);  
}

//点在线段上   
bool on( point p, line l )  
{  
    if ( l.dx*(p.y-l.y)-l.dy*(p.x-l.x) == 0 )  
    if ( (p.x-l.x)*(p.x-l.x-l.dx) <= 0 )  
    if ( (p.y-l.y)*(p.y-l.y-l.dy) <= 0 )  
        return true;  
    return false;  
}  
  
//点在多边形内  
bool in( point p, point* P, int n )  
{  
    double d[4][2] = {-101,-103,-103,101,101,-103,101,103};  
    for ( int t = 0 ; t < 4 ; ++ t ) {  
        line s1 = line( p, point( d[t][0], d[t][1] ) );  
        int  count = 0;  
        for ( int i = 0 ; i < n ; ++ i ) {  
            line s2 = line( P[i], P[i+1] );  
            if ( on( p, s2 ) ) return true;  
            if ( s_cross_s( s1, s2 ) ) count ++;  
            if ( on( P[i], s1 ) && l_cross_s( s1, line( P[i+1], P[(i-1+n)%n] ) ) ) count ++;  
        }
        if ( count%2 == 0 ) return false;   
    }  
    return true;  
}  

//两直线交点 
point crosspoint( line l, line m )
{
    point a = point( m.x, m.y );
    point b = point( m.x+m.dx, m.y+m.dy );
    if ( m.dx*l.dy == m.dy*l.dx ) {
        if ( dist( point( l.x, l.y ), a ) < dist( point( l.x, l.y ), b ) )
            return a;
        else return b;
    }else {
        double a1 = -l.dy,b1 = l.dx,c1 = l.dx*l.y-l.dy*l.x;
        double a2 = -m.dy,b2 = m.dx,c2 = m.dx*m.y-m.dy*m.x;
        double x = (c1*b2-c2*b1)/(a1*b2-a2*b1);
        double y = (c1*a2-c2*a1)/(b1*a2-b2*a1);
        return point( x, y );
    } 
}

//计算空的最大长度 
double Calcul( int N )
{
	H[N] = H[0];
	
	point  X[ 21 ];
	double D = 0.0;
	for ( int i = 0 ; i < N ; ++ i )
	for ( int j = i+1 ; j < N ; ++ j ) {
		line l = line( H[i], H[j] );
			
		int S = 0;
		for ( int k = 0 ; k < N ; ++ k ) {
			line m = line( H[k], H[k+1] );
			if ( l_cross_s( l, m ) )
				X[S ++] = crosspoint( l, m );
		}
		X[S ++] = H[i];
		X[S ++] = H[j];
		
		P0 = point( -101, -103 );
		for ( int k = 0 ; k < S ; ++ k )
			X[k].d = dist( P0, X[k] );
		sort( X, X+S, cmp2 );
		
		double sum = 0.0;
		int    fla = 0;
		for ( int i = 1 ; i < S ; ++ i ) {
			if ( in( point( (X[i-1].x+X[i].x)/2, (X[i-1].y+X[i].y)/2 ), H, N ) ) {
				if ( fla ) sum += dist( X[i-1], X[i] );
				else sum = dist( X[i-1], X[i] );
				D = max( D, sum );
				fla = 1;
			}
		}
	}
	
	return D;
}

int main()
{
    int T,N,M;
    while ( scanf("%d",&T) != EOF ) 
    while ( T -- ) {
        scanf("%d",&N);
        for ( int i = 0 ; i < N ; ++ i )
            scanf("%lf%lf",&H[i].x,&H[i].y);
        scanf("%d",&M);
        for ( int i = 0 ; i < M ; ++ i )
            scanf("%lf%lf",&C[i].x,&C[i].y);
        
        //计算硬币最小宽度 
		double d = Graham( M );
        
        //计算空的最大长度 
        double D = Calcul( N );
        
        if ( d <= D ) printf("legal\n");
        else printf("illegal\n");
    }
    return 0;
}

参考:http://blog.csdn.net/mobius_strip/article/details/8605377


  1. 老实说,这种方法就是穷举,复杂度是2^n,之所以能够AC是应为题目的测试数据有问题,要么数据量很小,要么能够得到k == t,否则即使n = 30,也要很久才能得出结果,本人亲测

  2. 第二个方法挺不错。NewHead代表新的头节点,通过递归找到最后一个节点之后,就把这个节点赋给NewHead,然后一直返回返回,中途这个值是没有变化的,一边返回一边把相应的指针方向颠倒,最后结束时返回新的头节点到主函数。

  3. 嗯 分析得很到位,确实用模板编程能让面试官对你的印象更好。在设置辅助栈的时候可以这样:push时,比较要push的elem和辅助栈的栈顶,elem<=min.top(),则min.push(elem).否则只要push(elem)就好。在pop的时候,比较stack.top()与min.top(),if(stack.top()<=min.top()),则{stack.pop();min.pop();},否则{stack.pop();}.

  4. 第二个方法挺不错。NewHead代表新的头节点,通过递归找到最后一个节点之后,就把这个节点赋给NewHead,然后一直返回返回,中途这个值是没有变化的,一边返回一边把相应的指针方向颠倒,最后结束时返回新的头节点到主函数。

  5. 第2题,TCP不支持多播,多播和广播仅应用于UDP。所以B选项是不对的。第2题,TCP不支持多播,多播和广播仅应用于UDP。所以B选项是不对的。