[问题描述]
某小区决定在小区内部建一家便利店,现小区内部共有八栋楼,它们的地理坐标分别为:(10,20) (30,34) (19,25) (38,49.1) (9,38.1) (2,34) (5,8) (29,48)。同时,其中的住户人数分别为:30, 45, 28, 8, 36, 16, 78, 56。为了方便更多的住户购物,要求实现总体最优,请问便利店应该建立在哪里?
【提示】
1)便利店无论选址何处,八栋楼的居民均可直接到达,即八栋楼与便利店均相邻,且距离为直线距离;
2)八栋楼的居民人数为权重,应该方便大多数人,实现总体最优。
下面是第一次写的做法 不精确
解题思路:对便利店的x 和y 分别使用三分法 先用x三分确定y 再用y三分确定x 代码如下:
#include <iostream>
#include <cmath>
using namespace std;
struct building
{
double x;
double y;
double value;
};
building bd[1000];
int n;//n栋楼
double minx,maxx,miny,maxy;//记录各栋楼的区域
double midx,mmidx,midy,mmidy;
double result_x,result_y,sum = 100000;//最后结果
double dis(double x,double y)//计算距离
{
double sum= 0;
for(int i = 0;i < n;i++)
{
sum += sqrt((x - bd[i].x)*(x - bd[i].x) + (y - bd[i].y)*(y - bd[i].y))*bd[i].value;
}
return sum;
}
void D_Divide()//三分法求位置
{
midx = (minx + maxx)/2;
mmidx = (midx + maxx)/2;
midy = (miny + maxy)/2;
mmidy = (midy + maxy)/2;
while((maxx-minx) > 0.01)
{
while((maxy-miny)>0.01)
{
if(dis(midx,midy) > dis(midx,mmidy))
miny = midy;
else
maxy = mmidy;
midy = (miny + maxy)/2;
mmidy = (midy + maxy)/2;
};
if(dis(midx,midy) > dis(mmidx,midy))
minx = midx;
else
maxx = mmidx;
midx = (minx + maxx)/2;
mmidx = (midx + maxx)/2;
};
result_x = midx;
result_y = midy;
sum = dis(result_x,result_y);
}
int main()
{
cout<<"请输入楼的数量:";
cin>>n;
cout<<"\n请输入各楼x y 权值"<<endl;
minx = maxx = miny = maxy = 0;
for(int i = 0;i < n;i++)
{
cin>>bd[i].x>>bd[i].y>>bd[i].value;
if(bd[i].x < minx)
minx = bd[i].x;
if(bd[i].x > maxx)
maxx = bd[i].x;
if(bd[i].y < minx)
miny = bd[i].y;
if(bd[i].y > maxx)
maxy = bd[i].y;
}
D_Divide();
cout<<"\n便利店选址坐标为:"<<endl;
cout<<"x: "<<result_x<<" "<<"y: "<<result_y<<endl;
cout<<"\n最优解为: "<<sum<<endl;
return 0;
}
/*
8
10 20 30
30 34 45
19 25 28
38 49.1 8
9 38.1 36
2 34 16
5 8 78
29 48 56
便利店选址坐标为 :
x:16.5379 y:27.573
最优解 ;5146.96
*/
以上解法有一点误差 因为每次 if(dis(midx,midy) > dis(midx,mmidy)) 时使用的都是midx 的最佳y 下面还把这样求得的y当做了mmidx的最佳y
下面是改正后的代码
#include <iostream>
#include <cmath>
using namespace std;
struct building
{
double x;
double y;
double value;
};
building bd[1000];
int n;//n栋楼
double minx,maxx,miny,maxy;//记录各栋楼的区域
double midx,mmidx,midy,mmidy;
double result_x,result_y,sum = 100000;//最后结果
double dis(double x,double y)//计算距离
{
double sum= 0;
for(int i = 0;i < n;i++)
{
sum += sqrt((x - bd[i].x)*(x - bd[i].x) + (y - bd[i].y)*(y - bd[i].y))*bd[i].value;
}
return sum;
}
double Divide_y(double x)//三分法求y
{
double right_y,left_y;
right_y = maxy;
left_y = miny;
midy = (left_y + right_y)/2;
mmidy = (midy + right_y)/2;
while((right_y-left_y)>0.01)
{
if(dis(x,midy) > dis(x,mmidy))
left_y = midy;
else
right_y = mmidy;
midy = (left_y + right_y)/2;
mmidy = (midy + right_y)/2;
};
return dis(x,midy);
}
void D_Divide()//三分法求x
{
midx = (minx + maxx)/2;
mmidx = (midx + maxx)/2;
while((maxx-minx) > 0.01)
{
if(Divide_y(midx) > Divide_y(mmidx))
minx = midx;
else
maxx = mmidx;
midx = (minx + maxx)/2;
mmidx = (midx + maxx)/2;
};
result_x = midx;
result_y = midy;
sum = dis(result_x,result_y);
}
int main()
{
cout<<"请输入楼的数量:";
cin>>n;
cout<<"\n请输入各楼x y 权值"<<endl;
minx = maxx = miny = maxy = 0;
for(int i = 0;i < n;i++)
{
cin>>bd[i].x>>bd[i].y>>bd[i].value;
if(bd[i].x < minx)
minx = bd[i].x;
if(bd[i].x > maxx)
maxx = bd[i].x;
if(bd[i].y < minx)
miny = bd[i].y;
if(bd[i].y > maxx)
maxy = bd[i].y;
}
D_Divide();
cout<<"\n便利店选址坐标为:"<<endl;
cout<<"x: "<<result_x<<" "<<"y: "<<result_y<<endl;
cout<<"\n最优解为: "<<sum<<endl;
return 0;
}
/*
8
10 20 30
30 34 45
19 25 28
38 49.1 8
9 38.1 36
2 34 16
5 8 78
29 48 56
便利店选址坐标为 :
x:16.5406 y:27.4346
最优解 ;5146.85
*/