题目描述
竞赛题 C 平面最佳坐标点 Time Limit:3000MS Memory Limit:65535K 题型: 编程题 语言: 无限制 描述 在平面坐标上,给n个坐标点,现定义两个点(x1,y1)和(x2,y2)的“距离”为|x1-x2|3+|x1-x2|*|y1-y2|+|y1-y2|3 现要求编程找一个点,使得n个点到该点的距离累加和最小,精度去到小数点3位 输入格式 第一行一个数n(n<=1000), 此后n行,每行两个浮点数为坐标点x y(0<=x,y<=1000) 输出格式 输出最佳位置坐标 输入样例 3 0 0 100 0 100 100 输出样例 58.648 41.352 Provider admin
解题思路及其过程
1.暴力法,直接暴力搜索答案(超时)
#include <iostream>
#include <cstdio>
#include <utility>
#include <vector>
#include <math.h>
using namespace std;
double cal(double x1,double y1,double x2,double y2){
return fabs(x1-x2)*fabs(x1-x2)*fabs(x1-x2)+fabs(x1-x2)*fabs(y1-y2)+fabs(y1-y2)*fabs(y1-y2)*fabs(y1-y2);
}
const int M=1000+5;
pair<double,double> a[M];
int main(){
int n;scanf("%d",&n);
int N=n;
int idx=0;
while(N--){
double x,y;scanf("%lf%lf",&x,&y);
a[idx].first=x;
a[idx++].second=y;
}
double minInt=0x3f3f3f3f;
pair<int,int> index;
for(int x=0;x<=1000;x++){
for(int y=0;y<=1000;y++){
double sumnow=0;
for(int i=0;i<idx;i++){
double x1=a[i].first,y1=a[i].second;
sumnow+=cal(x,y,x1,y1);
}
if(sumnow<minInt){
minInt=sumnow;
index.first=x;
index.second=y;
}
}
}
pair<double,double> ans;
for(double x=index.first-1;x<index.first;x+=0.001){
for(double y=index.second-1;y<index.second;y+=0.001){
double sumnow=0;
for(int i=0;i<idx;i++){
double x1=a[i].first,y1=a[i].second;
sumnow+=cal(x,y,x1,y1);
}
if(sumnow<minInt){
minInt=sumnow;
ans.first=x;
ans.second=y;
}
}
}
for(double x=index.first-1;x<index.first;x+=0.001){
for(double y=index.second+1;y>index.second;y-=0.001){
double sumnow=0;
for(int i=0;i<idx;i++){
double x1=a[i].first,y1=a[i].second;
sumnow+=cal(x,y,x1,y1);
}
if(sumnow<minInt){
minInt=sumnow;
ans.first=x;
ans.second=y;
}
}
}
for(double x=index.first+1;x>index.first;x-=0.001){
for(double y=index.second+1;y>index.second;y-=0.001){
double sumnow=0;
for(int i=0;i<idx;i++){
double x1=a[i].first,y1=a[i].second;
sumnow+=cal(x,y,x1,y1);
}
if(sumnow<minInt){
minInt=sumnow;
ans.first=x;
ans.second=y;
}
}
}
for(double x=index.first+1;x>index.first;x-=0.001){
for(double y=index.second-1;y<index.second;y+=0.001){
double sumnow=0;
for(int i=0;i<idx;i++){
double x1=a[i].first,y1=a[i].second;
sumnow+=cal(x,y,x1,y1);
}
if(sumnow<minInt){
minInt=sumnow;
ans.first=x;
ans.second=y;
}
}
}
printf("%.3lf %.3lf",ans.first,ans.second);
}
2.三分极值法
三分极值法是一种可以用于查找凸函数极值点的办法,一般用在指定函数区间内只有一个凸点的情况,它实际上是二分查找思想的延续,二分查找也是逐渐压缩搜索区间,三分极值也是如此,只不过是找中点左右的三分点。以此来加快搜索速度,时间复杂度是O(logn)
AC代码(100ms左右)
#include <iostream>
#include <cstdio>
#include <utility>
#include <vector>
#include <math.h>
using namespace std;
const int M=1000+5;
const double eps=0.0001;
pair<double,double> a[M];
int n;
double cal(double x1,double y1,double x2,double y2){
return fabs(x1-x2)*fabs(x1-x2)*fabs(x1-x2)+fabs(x1-x2)*fabs(y1-y2)+fabs(y1-y2)*fabs(y1-y2)*fabs(y1-y2);
}
double SumCal(double x,double y){
double sumNow=0;
for(int i=0;i<n;i++){
sumNow+=cal(x,y,a[i].first,a[i].second);
}
return sumNow;
}
int main(){
scanf("%d",&n);
int N=n;
int idx=0;
while(N--){
double x,y;scanf("%lf%lf",&x,&y);
a[idx].first=x;
a[idx++].second=y;
}
double XL,XR,XM,XMM,Xt1,Xt2;
double YL,YR,YM,YMM,Yt1,Yt2;
XL=0,XR=1000;
while(XR-XL >= eps){
XM=(XL+XR)/2;
XMM=(XM+XR)/2;
YL=0,YR=1000;
while(YR-YL>=eps){
YM=(YL+YR)/2;
YMM=(YM+YR)/2;
Yt1=SumCal(XM,YM);
Yt2=SumCal(XM,YMM);
if(Yt1<=Yt2){
YR=YMM;
}else{
YL=YM;
}
}
Xt1=SumCal(XM,YL);
YL=0,YR=1000;
while(YR-YL>=eps){
YM=(YL+YR)/2;
YMM=(YM+YR)/2;
Yt1=SumCal(XMM,YM);
Yt2=SumCal(XMM,YMM);
if(Yt1<=Yt2){
YR=YMM;
}else{
YL=YM;
}
}
Xt2=SumCal(XMM,YL);
if(Xt1<=Xt2){
XR=XMM;
}else{
XL=XM;
}
}
printf("%.3lf %.3lf",XL,YR);
}
3.模拟退火算法
学习链接 算法步骤 1.初始化:起始温度,终止温度,温度变化率,最优路径顶点集 ,最短长度S 2.利用蒙特卡洛法得到一个比较好的初始解 3.当前温度大于终止温度时,利用两点交换法在当前最优路径基础上构造一条新路径,计算新路径的长度S1,差值dE= S1 - S 4.若 dE < 0,当前最优路径更新为新路径,最短长度更新为S1 否则,任意产生一个介于0~1的概率rt,并计算exp(-dE/T),T为当前温度;若exp(-dE/T) > rt, 当前最优路径更新为新路径,最短长度更新为S1,更新温度
|