有一段时间没有做ACM算法题目了,今天正好有空便随便挑了209题来做做:ACM UVa算法题#209题
这道题有几个要点:
1. 给定坐标系
坐标系很容易定,我采用的是第一个点为(0, 0)点,X方向差别为2个单位,Y方向差别为1个单位,点之间的距离,也就是LEN为1个单位,这样便于计算。注意我用的不是实际长度,而是抽象的单位,这个单位在不同方向上面意义不一样,否则很容易通过三角形相关公理推出这样的三角形不存在,我们关心的只是这样的一个对应关系。这里的人为设定确实有些Confusing,我之前也是按照一般的三角形的长度,如3,4,5来定义,但是后来发现这样做做的乘除法太多,过于浪费CPU Cycle,如果按照我这样的设定,大部分情况只用到加减法,另外一种情况只需用到移位操作即可。
参看下图:
2. 判断是否两点连线是一条边(Coincide)
这里可以分两种情况:
a. Y1 = y2, 必然是Coincide
b. 否则,X_Delta = abs(x1 – x2), Y_Delta = abs(y1 – y2), 由于之前我们人为设定X方向差别为2个单位,Y方向差别为1一个单位,因此只要X_Delta = Y_Delta即可
3. 计算距离
假定是Coincide的情况,否则直接返回出错,因为在非Coincide的情况无需计算距离。此外,由于这里已经知道是Coincide,并且我们并没有统一单位,所以这里不能也不应该用勾股定理来计算长度,而是采用比例的方法,同样分两种情况,参考上图:
a. Y1 = y2, 那么因为X方向上两个单位对应一个长度Unit,所以长度=abs(x1 – x2) >> 1;
b. 否则,长度Unit的个数和X/Y方向(任意)的差别相等,也就是长度=abs(x1 – x2)
4. 判断是否是目标图形,并且每条边相等
对于三角形,很简单,直接对3条边判断即可,没有什么变数。对于四边形和六边形就不同了,需要用到遍历来确定一个从某点开始(我们可以固定为第一个点)遍历所有点最后回到该点的环,并且每条边长度均相等,注意这里由于题目的特殊性,不用判断平行等条件。可以用一个邻接矩阵来代表对应的边的长度,这个应该一次性计算出来,如果非Coincide则设置为某个特殊值,比如0
刚开始提交的时候,Rank是45,之后我又做了下面的优化:
1. 当遍历尝试完毕从最初点出发的某条边的时候,说明这一边不可能成为环,将其置为0表示不可通,并且遍历从最初点出发的其他同样长度的边,置为0,减少遍历次数
2. 在初始化计算所有点的坐标的时候改变了一点点算法,用加减法代替乘法
3. 最初坐标采用的是实际的长度,而不是像上面那样用不同的抽象单位算出,修改之后减少了大量乘除法计算
4. 调整遍历算法,由于从初始点出发之后,后3个点必然不能是初始点,因此做了一点修改对这个情况作了优化
5. 修改对邻接矩阵的算法,由于adj[i][j] = adj[j][i],所以只需计算矩阵的一半即可
修改之后再提交Rank变成了33,似乎是目前个人的最好纪录 J
32 | 0.170 | 792 | C | 2002-02-04 07:02:47 | 732386 | |
33 | 0.172 | 1160 | C++ | 2007-05-02 16:05:54 | 5551868 | |
34 | 0.174 | 404 | C | 2001-08-30 08:46:54 | 539862 |
// ACM UVa Problem #209
// http://acm.uva.es/p/v2/209.html
//
// Author: ATField
// Email: atfield_zhang@hotmail.com
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 65535
struct point
{
public :
bool is_coincide(const point &pt) const
{
// not allow testing points with same coordinates
if( _x == pt._x && _y == pt._y )
return false;
if( _y == pt._y )
return true;
else
{
int k1 = abs( _x - pt._x );
int k2 = abs( _y - pt._y );
if( k1 == k2 )
return true;
}
return false;
}
int get_dist(const point &pt) const
{
// not allow testing points with same coordinates
if( _x == pt._x && _y == pt._y )
return 0;
if( _y == pt._y )
return abs(_x - pt._x) >> 1; // need to divide by 2
else
{
int k1 = abs( _x - pt._x );
int k2 = abs( _y - pt._y );
if( k1 == k2 )
return k1;
}
return 0;
}
public :
static const int X_DELTA = 2;
static const int Y_DELTA = 3;
static const int LEN = 4;
public :
int _x;
int _y;
int _valid;
private :
static point s_all_points[MAX];
public :
static void prepare()
{
int level = 0;
int before_next_level = 1;
int next_x = 0;
int next_y = 0;
for( int i = 1; i < MAX ; ++i )
{
s_all_points[i]._x = next_x;
s_all_points[i]._y = next_y;
before_next_level--;
if( before_next_level == 0 )
{
level++;
before_next_level = level + 1;
next_x = -level;
next_y++;
}
else
next_x += 2;
}
}
static point *all_points()
{
return s_all_points;
}
};
point point::s_all_points[MAX];
bool check_triangle(int *n)
{
// 1. Duplicates
// No need to check duplicate because the following checks will do
// 2. Coincide
// Check every edge
point *points = point::all_points();
if( points[n[0]].is_coincide(points[n[1]]) &&
points[n[0]].is_coincide(points[n[2]]) &&
points[n[1]].is_coincide(points[n[2]]) )
{
// 3. Same length
// Check every edge
int len = points[n[0]].get_dist(points[n[1]]);
if( len == points[n[0]].get_dist(points[n[2]]) &&
len == points[n[1]].get_dist(points[n[2]]) )
return true;
}
return false;
}
bool init_adj(int *n, int num, int adj[6][6])
{
point *points = point::all_points();
for( int i = 0; i < num; ++i )
{
for( int j = i; j < num; ++j )
{
if( i == j )
{
adj[i][j] = 0;
adj[j][i] = 0;
}
else
{
// 1. Duplicates
// Return false when found duplicate
if( n[i] == n[j] )
return false;
// 2. Coincide & Len (When not coincide, len = 0)
adj[i][j] = points[n[i]].get_dist(points[n[j]]);
adj[j][i] = adj[i][j];
}
}
}
return true;
}
int find_same_len_loop(int adj[6][6], int num)
{
int stack[6];
int used[6];
int next[6];
for( int i = 1; i < num; ++i )
{
int len = adj[0][i];
// skip non-connected dots
if( len == 0 ) continue;
// initialize "used" array & "next" array
for( int j = 0; j < num; ++j )
{
used[j] = 0;
}
int top = 0;
stack[0] = i;
used[i] = 1;
next[0] = -1;
top++;
while( top > 0 )
{
next[top-1]++;
// checked all the connected points?
if( next[top-1] >= num )
{
// yes, then pop the stack
top--;
used[stack[top]] = 0;
}
else
{
int next_point = next[top-1];
// follow non-used, same-length edge
if( used[next_point] == 0 && len == adj[stack[top-1]][next_point] )
{
stack[top] = next_point;
used[next_point] = 1;
// don't allow pushing 0 into stack before the last point
if( top < num - 1 )
next[top] = 0;
else
next[top] = -1;
top++;
// stack is full? found a loop
if( top == num )
return len;
}
}
}
// if this doesn't work, delete all edges starting from id 0 of this len
for( int j = i; j < num; ++j )
if( adj[0][j] == len )
adj[0][j] = 0;
}
return 0;
}
bool check_parallelogram(int *n)
{
int adj[6][6];
if(!init_adj(n, 4, adj))
return false; // found duplicate
if(find_same_len_loop(adj, 4))
return true;
return false;
}
bool check_hexagon(int *n)
{
int adj[6][6];
if(!init_adj(n, 6, adj))
return false; // found duplicate
if(find_same_len_loop(adj, 6))
return true;
return false;
}
int main(int argc, char *argv[])
{
point::prepare();
while(1)
{
char input[255];
if( gets(input) == NULL )
break;
else if( strlen(input) == 0 )
break;
int n[6];
int fields = sscanf( input, "%d %d %d %d %d %d", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5] );
printf("%s ", input);
switch(fields)
{
case 3:
{
bool is_triangle = check_triangle(n);
if( is_triangle )
{
puts("are the vertices of a triangle");
continue;
}
break;
}
case 4:
{
bool is_parallelogram = check_parallelogram(n);
if( is_parallelogram )
{
puts("are the vertices of a parallelogram");
continue;
}
break;
}
case 6:
{
bool is_hexagon = check_hexagon(n);
if( is_hexagon )
{
puts("are the vertices of a hexagon");
continue;
}
break;
}
}
puts("are not the vertices of an acceptable figure");
}
return 0;
}
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1595174