ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[LOJ 6360] 复燃「恋之埋火」

2021-07-22 20:03:19  阅读:233  来源: 互联网

标签:复燃 LOJ 最小 埋火 int 圆心 2A include 向量


一、题目

点此看题

二、解法

前置知识:最小圆覆盖高斯消元求圆心

根据随机增量法的复杂度分析,我们发现就算在高维情况它也是 \(O(n)\) 的,问题在于 \(m\) 维空间,给定 \(k+1\) 个在圆上的点,怎么求覆盖它们的最小圆?可以考虑高斯消元,但要推柿子。

结论:最小圆的圆心一定要在这 \(k+1\) 个点构成的平面上,也就是说以某个点为原点构建出 \(k\) 个向量基底,圆心要能被这 \(k\) 个基底表示出来,因为这样圆的半径才能最小,翻译成数学语言,设向量 \(x\) 为圆心,\(x_i\) 表示给定的点:

\[x-x_0=\sum_{i=1}^k w_i(x_i-x_0) \]

\[2A^TAw=b-2A^Tx_0 \]

现在就可以直接算了,如果你喜欢还可以化简成下面的形式:

\[(b-2A^Tx_0)_i=|x_i|^2-|x_0|^2-2x_i^T\cdot x_0+2|x_0|^2=|x_i-x_0|^2 \]

\(2A^TA\) 就是我们的系数矩阵,\(|x_i-x_0|^2\) 是等号右边的东西,解出来 \(w\) 向量就可以算出圆心坐标了。

三、总结

由于最小圆覆盖的复杂度证明它在高维情况下也适用。

推柿子的时候可以把向量写成矩阵的形式。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;
const int M = 20005;
#define db double
#define eps 1e-7
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m;db r;
struct point
{
	db x[6];
	point() {for(int i=0;i<m;i++) x[i]=0;}
	point operator + (point b)
	{
		point r;
		for(int i=0;i<m;i++)
			r.x[i]=x[i]+b.x[i];
		return r;
	}
	point operator - (point b)
	{
		point r;
		for(int i=0;i<m;i++)
			r.x[i]=x[i]-b.x[i];
		return r;
	}
	point operator * (db t)
	{
		point r;
		for(int i=0;i<m;i++)
			r.x[i]=x[i]*t;
		return r;
	}
	db len()
	{
		db r=0;
		for(int i=0;i<m;i++)
			r+=x[i]*x[i];
		return r;
	}
}p[M],e[9],o;
db sqr(db x) {return x*x;}
db Abs(db x) {return x>0?x:-x;}
point cir(int n)
{
	db a[9][9]={},b[9][9]={},c[9][9]={};
	if(n==0) return e[0];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			a[i][j]=b[j][i]=e[i].x[j-1]-e[0].x[j-1];
			c[i][n+1]+=sqr(e[i].x[j-1]-e[0].x[j-1]);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=n;k++)
				c[i][k]+=a[i][j]*b[j][k];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			c[i][j]*=2;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
			if(Abs(c[i][i])<eps && Abs(c[j][i])>eps)
			{
				swap(c[i],c[j]);
				break;
			}
		for(int j=1;j<=n;j++)
		{
			if(Abs(c[j][i])<eps || i==j) continue;
			db t=c[j][i]/c[i][i];
			for(int k=1;k<=n+1;k++)
				c[j][k]-=t*c[i][k];
		}
	}
	point r=e[0];
	for(int i=1;i<=n;i++)
		r=r+(e[i]-e[0])*(c[i][n+1]/c[i][i]);
	return r;
}
void dfs(int u,int v,int lim)
{
	if(v==lim) return ;
	if((p[v]-o).len()>r)
	{
		e[u]=p[v];
		o=cir(u);r=(p[v]-o).len();
		if(u<m) dfs(u+1,0,v);//adjust from begin
	}
	dfs(u,v+1,lim);//move on
}
signed main()
{
	n=read();m=read();
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			scanf("%lf",&p[i].x[j]);
	random_shuffle(p,p+n);
	dfs(0,0,n);
	for(int i=0;i<m;i++)
		printf("%.8f ",o.x[i]);
}

标签:复燃,LOJ,最小,埋火,int,圆心,2A,include,向量
来源: https://www.cnblogs.com/C202044zxy/p/15045992.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有