Problem Address:http://poj.org/problem?id=1228
【前言】
顺手又找了一道题。
后来各种WA。
WA到第五次。WA到不知所措。
忽然看到……距离公式的一个字母打错了……
然后各种A。
A到第五次WA的版本。
A到后来改写的一个版本。
前者16MS。后者0MS。
【思路】
这个题意真不好理解。
意思为根据给定的凸多边形,判断其是否为唯一,即每条边存在三个以上的点。
可以这样解释,如果某条边只有两个顶点,那么如果加多一个点就会多出一个角,这样的话,如果要使该凸多边形唯一,必须规定每条边有三个以上的点。
一种方法是把所有点组成凸多边形,然后在栈的内部逐个判断。
另一个种是构成严格的凸多边形,即不包括边上的非顶点。然后循环点集以及比较点栈,计算每条边的点数。不过要特别处理最后一条边。
或者说两种方法都要特别注意最后一条边。
此外还要注意一条直线的情况是返回NO的。
还有n<=5的时候也是返回NO的。
两种方法,后者写起来思路比较清晰,写法也很清晰,时间上也有点优势。
不过大部分代码都是类似的。
【代码】
第一种方法:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1000;
struct point
{
int x;
int y;
}pt[maxn+5], q[maxn+5];
bool cmp(const point &a, const point &b)
{
int ax = a.x-pt[0].x;
int ay = a.y-pt[0].y;
int bx = b.x-pt[0].x;
int by = b.y-pt[0].y;
int t = ax*by - bx*ay;
if (t<0) return 0;
else if (t>0) return 1;
else return (ax*ax+ay*ay)<(bx*bx+by*by); //就是这里有一个bx敲成by……
}
int main()
{
int t;
int n;
int i, k;
point temp;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for (i=0; i<n; i++)
scanf("%d %d", &pt[i].x, &pt[i].y);
if (n<=5)
{
printf("NO\n");
continue;
}
for (i=1, k=0; i<n; i++)
{
if (pt[i].y<pt[k].y)
{
k = i;
}
else if (pt[i].y==pt[k].y)
{
if (pt[i].x<pt[k].x)
{
k = i;
}
}
}
temp = pt[0];
pt[0] = pt[k];
pt[k] = temp;
sort(pt+1, pt+n, cmp);
q[0] = pt[0];
q[1] = pt[1];
q[2] = pt[2];
int top = 3;
pt[n] = pt[0];
for (i=3; i<=n; i++)
{
while((pt[i].x-q[top-2].x)*(q[top-1].y-q[top-2].y)-(q[top-1].x-q[top-2].x)*(pt[i].y-q[top-2].y)>0 && top>2)
{
top--;
}
q[top] = pt[i];
top++;
}
int ct = 2;
for (i=1; i<top-1; i++)
{
if ((q[i].x-q[i-1].x)*(q[i+1].y-q[i-1].y)==(q[i+1].x-q[i-1].x)*(q[i].y-q[i-1].y))//每个点都要计算,浪费时间
{
ct++;
}
else
{
if (ct>=3)
ct = 2;
else
break;
}
}
if (i==top-1)
{
ct = 2;
for (k=n-2; k>=0; k--)
{
if ((pt[k].x-pt[n-1].x)*(pt[n].y-pt[n-1].y)==(pt[n].x-pt[n-1].x)*(pt[k].y-pt[n-1].y))
ct++;
else
break;
}
}
if (i<top-1 || ct==n+1 || ct<3)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}
第二种方法:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1000;
struct point
{
int x;
int y;
}pt[maxn+5], q[maxn+5];
int edge[maxn+5];
bool cmp(const point &a, const point &b)
{
int ax = a.x-pt[0].x;
int ay = a.y-pt[0].y;
int bx = b.x-pt[0].x;
int by = b.y-pt[0].y;
int t = ax*by - bx*ay;
if (t<0) return 0;
else if (t>0) return 1;
else return (ax*ax+ay*ay)<(bx*bx+by*by);
}
int main()
{
int t;
int n;
int i, j, k;
point temp;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for (i=0; i<n; i++)
scanf("%d %d", &pt[i].x, &pt[i].y);
if (n<=5)
{
printf("NO\n");
continue;
}
for (i=1, k=0; i<n; i++)
{
if (pt[i].y<pt[k].y)
{
k = i;
}
else if (pt[i].y==pt[k].y)
{
if (pt[i].x<pt[k].x)
{
k = i;
}
}
}
temp = pt[0];
pt[0] = pt[k];
pt[k] = temp;
sort(pt+1, pt+n, cmp);
q[0] = pt[0];
q[1] = pt[1]; //严格凸包,所以只有p0,p1入栈,且下面使用top>1
int top = 2;
pt[n] = pt[0];
for (i=2; i<=n; i++)
{
while(top>1 && (pt[i].x-q[top-2].x)*(q[top-1].y-q[top-2].y)-(q[top-1].x-q[top-2].x)*(pt[i].y-q[top-2].y)>=0)
{
top--;
}
q[top] = pt[i];
top++;
}
for (k=n-2; k>=0; k--)//查找最后一条边的点集
{
if ((pt[k].x-pt[n-1].x)*(pt[n].y-pt[n-1].y)!=(pt[n].x-pt[n-1].x)*(pt[k].y-pt[n-1].y)) break;
}
for (i=k+1, j=n-1; i<j; i++,j--)//重新排列最后一条边
{
temp = pt[i];
pt[i] = pt[j];
pt[j] = temp;
}
memset(edge, 0, sizeof(edge));
for (i=0,k=0; i<=n; i++)//计算边上的点数,由于点集是有序的,所以计算起来很方便
{
if (q[k+1].x==pt[i].x && q[k+1].y==pt[i].y)//直接判断,节省时间
{
edge[k]++;
k++;
edge[k]++;
}
else
{
edge[k]++;
}
}
if (top==2)//排除一条直线的情况
{
printf("NO\n");
continue;
}
for (i=0; i<top-1; i++)
{
if (edge[i]<3) break;
}
if (i<top-1) printf("NO\n");
else printf("YES\n");
}
return 0;
}
【P.S】
几何题,注定要WA的。
明天做道凸包求最远点对的题。