Solução do problema de casco convexo pelo método de dividir e conquistar

Introdução do problema

Dado um conjunto de alguns pontos no plano, encontre alguns pontos para que eles formem um invólucro convexo, envolvendo todos os pontos, conforme mostrado na figura
Insira a descrição da imagem aqui

Idéias

Usando o método de dividir e conquistar, o conjunto de pontos é dividido em dois, e o problema geral do casco convexo pode ser dividido em [encontre o casco convexo da metade superior] + [encontre o casco convexo da metade inferior]

Subestratégia

A estratégia para dividir o conjunto em dois é classificar os pontos em ordem crescente por x, e em ordem crescente por y para o mesmo x e, em seguida, selecione 0 e o último índice para fazer uma linha reta com esses dois pontos. À direita , usamos pa e pb para denotar esses dois pontos
Insira a descrição da imagem aqui

Divida o escopo da solução do subproblema

O conjunto foi dividido em dois; então, como o intervalo de solução do subproblema deve ser delimitado?

Atravessar todos os pontos

  • Encontre o ponto pmax mais distante da "linha reta pa pb" acima da "linha reta pa pb"
  • Encontre o ponto pmin mais distante de [linha reta pa pb] em [linha reta pa pb]

A maior distância

Essa distância pode ser expressa pela área do triângulo formada por seus três pontos. Esse determinante pode ser resolvido. Vale a pena notar que p1 deve estar à esquerda de p2, p1 e p2 formam uma linha reta e p3 é o ponto que queremos julgar.

  • O valor final é maior que 0, indicando que p3 está acima [linha reta p1 p2]
  • O valor final é menor que 0, indicando que p3 está abaixo [linha reta p1 p2]
  • O valor final é igual a 0, indicando que p3 em [linha reta p1 p2]
    Insira a descrição da imagem aqui
    encontramos o ponto pmax acima de [linha reta pa pb], a maior distância de [linha reta pa pb] e abaixo de [linha reta pa pb] e abaixo de [linha reta pa pb], a distância [ Linha reta pa pb] O ponto mais distante pmin, conecte pa, pb, pmax, pmin para obter a seguinte figura:
    Insira a descrição da imagem aqui
    Pegue o ponto acima [linha reta pa pmax] como o conjunto s1 para a próxima pesquisa e
    coloque o ponto acima [linha reta pmax pb] Como o próximo conjunto de pesquisa s2 , o ponto abaixo
    [linha reta pa pmin] é considerado o próximo conjunto de pesquisa s3 e o ponto abaixo [linha reta pmin pb] é considerado o próximo conjunto de pesquisa s4.

Insira a descrição da imagem aqui

Recursivamente coletar um conjunto de quatro pontos regionais, é importante notar que

Use a matriz de pontos para armazenar
pontos.Use a matriz vis para indicar se o ponto com índice i é um ponto no casco convexo e vis [i] = 1 é

  • Condições de contorno recursivas, o número de conjuntos de pontos é menor que 3, indicando que todos os pontos estão no casco convexo, vis é definido como 1
  • Os pontos na linha também devem ser adicionados ao próximo conjunto de pontos
  • Se a recursão é o conjunto da metade superior, todas as recursões subsequentes são apenas para a metade superior, recursiva s1 s2
  • Se a recursão for a metade inferior do conjunto, toda a recursão subsequente será apenas para a metade inferior, recursão s3 s4
  • Se a recursão for o conjunto inteiro (somente a primeira recursão enviará este caso), você precisará recursivamente as partes superior e inferior ao mesmo tempo, ou seja, recursivamente s1 s2 s3 s4

Código

Digite:

12
1 1
1 2
2 0
2 1
2 3
3 1
3 3
4 0
4 2
5 1
5 4
6 2

Saída

(1, 1)
(1, 2)
(2, 0)
(2, 3)
(4, 0)
(5, 1)
(5, 4)
(6, 2)
#include <bits/stdc++.h>

using namespace std;

// 结构定义 
typedef struct p
{
	int x, y;	
}p;

bool cmp(const p& p1, const p& p2)
{
	if(p1.x==p2.x) return p1.y<p2.y;
	return p1.x<p2.x;
}

// 存放点 
p points[100];
int vis[100];

// 计算p1,p2,pi三个点组成的三角形面积 
int peak(p p1, p p2, p pi)
{
	return p1.x*p2.y + pi.x*p1.y + p2.x*pi.y - pi.x*p2.y - p2.x*p1.y - p1.x*pi.y;
}

// 递归求凸包 
// ps 是当前要求解的点的集合,ps保存这些点在points数组中的下标 
// mode 表示递归s1 s2,还是递归s3,s4,还是同时递归 s1,s2,s3,s4
// mode = 3 递归 s1,s2,s3,s4,只有第一次调用会出现该情况 
// mode = 2 递归s3,s4
// mode = 1 递归s1 s2
void hull(vector<int> &ps, int mode)
{
	// 边界处理:少于两个点的集合一定是凸包上的点 
	if(ps.size()<=2)
	{
		for(int i=0; i<ps.size(); i++) vis[ps[i]]=1;
		return;
	}
	
	// 最左右一定是凸包上的点, pa最左点,pb最右点 
	vis[ps.front()]=1, vis[ps.back()]=1;
	p pa=points[ps.front()], pb=points[ps.back()];
	
	// 找距离 pa,pb组成的直线最远的点,imax是上方最远,imin是下方最远 
	int maxs=INT_MIN, mins=INT_MAX, imax=-1, imin=-1;
	for(int i=1; i<ps.size()-1; i++)
	{
		int s = peak(pa, pb, points[ps[i]]);
		if(s>maxs && s>=0) maxs=s, imax=ps[i];
		if(s<mins && s<=0) mins=s, imin=ps[i];
	}
	
	// pa,pb与imax,imin的连线,分割出下一趟递归的点集合 s1 s2 s3 s4
	vector<int> s1, s2, s3, s4;
	for(int i=0; i<ps.size()-1; i++)
	{
		if(peak(pa, points[imax], points[ps[i]])>=0) s1.push_back(ps[i]);
		if(peak(pa, points[imin], points[ps[i]])<=0) s3.push_back(ps[i]);
	}
	for(int i=1; i<ps.size(); i++)
	{
		if(peak(points[imax], pb, points[ps[i]])>=0) s2.push_back(ps[i]);
		if(peak(points[imin], pb, points[ps[i]])<=0) s4.push_back(ps[i]);
	}
	 
	if(mode==3)
		hull(s1, 1), hull(s2, 1), hull(s3, 2), hull(s4, 2);
	else if(mode==1) hull(s1, 1), hull(s2, 1);
	else if(mode==2) hull(s3, 2), hull(s4, 2);
} 

int main()
{
	
	int n, x, y;
	cin>>n;
	vector<int> ps(n);
	for(int i=0; i<n; i++)
		cin>>points[i].x>>points[i].y, ps[i]=i;
	sort(points, points+n, cmp);
	
	hull(ps, 3);
	
	for(int i=0; i<n; i++)
		if(vis[i]==1) cout<<"("<<points[i].x<<", "<<points[i].y<<")"<<endl;
		
	return 0;
}

/*
12
1 1
1 2
2 0
2 1
2 3
3 1
3 3
4 0
4 2
5 1
5 4
6 2
*/
Publicado 262 artigos originais · ganhou 11 · 10 mil visualizações

Acho que você gosta

Origin blog.csdn.net/weixin_44176696/article/details/105291858
Recomendado
Clasificación