C语言-Graham扫描算法-凸包问题

1.问题描述

某大学ACM集训队,不久前向学校申请了一块空地,成为自己的果园。全体队员兴高采烈的策划方案,种植了大批果树,有梨树、桃树、香蕉……。后来,发现有些坏蛋,他们暗地里偷摘果园的果子,被ACM集训队队员发现了。因此,大家商量解决办法,有人提出:修筑一圈篱笆,把果园围起来,但是由于我们的经费有限,必须尽量节省资金,所以,我们要找出一种最合理的方案。由于每道篱笆,无论长度多长,都是同等价钱。所以,大家希望设计出来的修筑一圈篱笆的方案所花费的资金最少。有人已经做了准备工序哦,统计了果园里果树的位置,每棵果树分别用二维坐标来表示,进行定位。现在,他们要求根据所有的果树的位置,找出一个n边形的最小篱笆,使得所有果树都包围在篱笆内部,或者在篱笆边沿上。

2.方法及基本思路

本题的实质:凸包问题。对于本问题具体来说:我选用了Graham扫描算法,根据之前学过的数学原理我们知道y值最小的坐标点一定在构成的凸包上,首先找到y坐标的点为枢纽点(如果有多个点拥有最小 y 坐标,则选择最左边的点),将其余坐标点与枢纽点的极角(与x轴正方向的夹角)从小到大排序(如果两点有相同的极角,则将距离枢纽点较远的排在前面),然后用一个栈,将枢纽点和排好序的第一个点入栈,依次将剩余的点与栈顶的两点做叉乘比较,最后输出比较后的凸包上的点。

3.算法描述

GrahamScan(points):

    pivot = 找到y坐标最小的点作为枢轴点(pivot)

    #对于points数组中的点,按照枢轴点的极角进行排序,如果极角相同,则距离远的点在前面

    # 创建一个空栈stack,用于存储构成凸包的点

    stack = new Point array of size n

    top = 0  # 栈顶指针初始化为0

    # 将前两个点放入栈中

    stack[top++] = points[0]

    stack[top++] = points[1]

    # 从第三个点开始遍历所有点

    for i = 2 to n-1:

        # 当栈中的点大于2个,并且当前点不在栈顶点的右侧或共线时,弹出栈顶点,直到不满足条件为止

        while top > 1 and points[i] is to the right or on the line formed by top two points in stack:

            pop stack[top-1]

        # 将当前点放入栈中

        stack[top++] = points[i]

    # 输出构成凸包的点

    output "Points forming the convex hull:"

    for i = 0 to top-1:

        output stack[i]

4.源代码

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 

typedef struct 
{
    int x, y;
} Point;

Point pivot;

double distance(Point a, Point b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
int crossProduct(Point a, Point b, Point c) 
{
    return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

int compareAngles(const void* vp1, const void* vp2)
{
    Point* p1 = (Point*)vp1;
    Point* p2 = (Point*)vp2;

    int orientation = crossProduct(pivot, *p1, *p2);
    if (orientation == 0)
        return distance(pivot, *p1) - distance(pivot, *p2);
    return (orientation > 0) ? -1 : 1;
}

void grahamScan(Point points[], int n) 
{
    int minIdx = 0;
    for (int i = 1; i < n; i++) {
        if (points[i].y < points[minIdx].y || (points[i].y == points[minIdx].y && points[i].x < points[minIdx].x))
        {
            minIdx = i;
        }
    }
    Point temp = points[0];
    points[0] = points[minIdx];
    points[minIdx] = temp;

    pivot = points[0];
    qsort(&points[1], n - 1, sizeof(Point), compareAngles);

    Point* stack = (Point*)malloc(n * sizeof(Point));
    int top = 0;
    stack[top++] = points[0];
    stack[top++] = points[1];

    for (int i = 2; i < n; i++) {
        while (top > 1 && crossProduct(stack[top - 2], stack[top - 1], points[i]) <= 0) 
        {
            top--;
        }
        stack[top++] = points[i];
    }

    printf("构成最小篱笆的点坐标为:\n");
    for (int i = 0; i < top; i++)
    {
        printf("(%d, %d)\n", stack[i].x, stack[i].y);
    }
    free(stack);
}

int main()
{
    int n;
    printf("请输入果树的数量:");
    scanf("%d", &n);

    Point* points = (Point*)malloc(n * sizeof(Point));
    if (points == NULL) {
        printf("内存分配失败");
        return 1;
    }

    for (int i = 0; i < n; i++)
    {
        printf("请输入第%d颗果树的坐标(格式:x y):", i + 1);
        scanf("%d %d", &points[i].x, &points[i].y);
    }

    grahamScan(points, n);

    free(points);
    return 0;
}

5.运行结果

C语言-Graham扫描算法-凸包问题_第1张图片

你可能感兴趣的:(C语言-算法分析与设计,c语言,算法,开发语言)