您的位置:首页 > 理论基础 > 数据结构算法

2013数据结构课程设计之便利店选址(暴力枚举或随机函数或三分)

2013-09-12 16:44 453 查看
[问题描述]

某小区决定在小区内部建一家便利店,现小区内部共有八栋楼,它们的地理坐标分别为:(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)八栋楼的居民人数为权重,应该方便大多数人,实现总体最优。

先是暴力找点的方法。

解题思路:自己开始想的就是暴力枚举,先找大范围,再找小范围。做这个题目就想到了的warmup2的1002题,但当时就是A不了。思路很简单,一步步地精确范围。先把整个地方划分成10*10的方格,再在里面找哪个最小,然后继续10*10每次都这样划分,精度确定跳出循环即可。详解见代码。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int n; //n栋楼
double minx,miny,maxx,maxy,px,py;  //找到四个边界
double ansx,ansy,sum;  //最后的结果
struct mq
{
double x;
double y;
int peo;
};
mq node[1005];

double cal(double px,double py)  //计算值
{
int i;
double sum=0;
for(i=0;i<n;i++)
sum+=sqrt((px-node[i].x)*(px-node[i].x)+(py-node[i].y)*(py-node[i].y))*node[i].peo;
return sum;
}

void solve()
{
double i,j;
double fenx=maxx-minx;  //把x分成若干份
double feny=maxy-miny;  //把y分成若干份

while(fenx>0.01&&feny>0.01)  //暴力找点
{
fenx/=10.0,feny/=10.0;
for(i=minx;i<=maxx;i+=fenx)
for(j=miny;j<=maxy;j+=feny)
{
double tmp=cal(i,j);
if(tmp<sum)
{
sum=tmp;
ansx=i;
ansy=j;
}
}
minx=ansx-fenx;
miny=ansy-feny;
maxx=ansx+fenx;
maxy=ansy+feny;
}
}

int main()
{
int i;
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
while(~scanf("%d",&n))  //n栋楼
{
scanf("%lf%lf%d",&node[0].x,&node[0].y,&node[0].peo);
minx=maxx=node[0].x; miny=maxy=node[0].y;
//找到四个边界
for(i=1;i<n;i++)
{
scanf("%lf%lf%d",&node[i].x,&node[i].y,&node[i].peo);
if(node[i].x<minx) minx=node[i].x;
if(node[i].x>maxx) maxx=node[i].x;
if(node[i].y<miny) miny=node[i].y;
if(node[i].y>maxy) maxy=node[i].y;
}

sum=100000000;
solve();
cout<<"便利店选址坐标为:"<<endl;
cout<<"x: "<<ansx<<"   "<<"y: "<<ansy<<endl;
cout<<"最优解为: "<<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.5404   y: 27.4362
最优解为: 5146.85
*/


然后就是LCM说的随机算法,也没想到那一块,被他一说是有些道理,但是点的范围一扩大的话,就没那么准确了。

随机算法,我是直接采用的随机数然后%lenth,区间宽度,相当于0~lenth-1。随机函数对范围要求比较严格吧,范围如果太大,随机也没用了。具体实现见代码:

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int n; //n栋楼
struct mq
{
double x;
double y;
int peo;
};
mq node[1005];

double cal(double px,double py)  //计算值
{
int i;
double sum=0;
for(i=0;i<n;i++)
sum+=sqrt((px-node[i].x)*(px-node[i].x)+(py-node[i].y)*(py-node[i].y))*node[i].peo;
return sum;
}
int main()
{
int i;
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
double minx,miny,maxx,maxy,px,py;  //找到四个边界
double ansx,ansy;  //最后的结果
while(~scanf("%d",&n))  //n栋楼
{
scanf("%lf%lf%d",&node[0].x,&node[0].y,&node[0].peo);
minx=maxx=node[0].x; miny=maxy=node[0].y;
//找到四个边界
for(i=1;i<n;i++)
{
scanf("%lf%lf%d",&node[i].x,&node[i].y,&node[i].peo);
if(node[i].x<minx) minx=node[i].x;
if(node[i].x>maxx) maxx=node[i].x;
if(node[i].y<miny) miny=node[i].y;
if(node[i].y>maxy) maxy=node[i].y;
}

minx*=100,maxx*=100,miny*=100,maxy*=100;  //边界扩大一百倍
//找到边界了就可以随机了
int lenx,leny;
lenx=ceil(maxx-minx),leny=ceil(maxy-miny);
double sum=1000000000;
srand((unsigned)time(NULL));  //播种
for(i=1;i<=500000;i++) //随机50W次
{
px=rand()%lenx+minx;
py=rand()%leny+miny;
px/=100.0;
py/=100.0;
double tmp=cal(px,py);
if(tmp<sum)
{
sum=tmp;
ansx=px;
ansy=py;
}
}
cout<<"便利店选址坐标为:"<<endl;
cout<<"x: "<<ansx<<"   "<<"y: "<<ansy<<endl;
cout<<"最优解为: "<<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.56   y: 27.44
最优解为: 5146.85
*/


然后是gcz在我们班群里面说的三分的方法,这个三分很久都没写过了,这个也是二维的三分。二分是解决单调性不变的函数,而三分则是解决类似抛物线这样的函数,有一个凸点或凹点。后来问了一下同学,思路是这样,先三分x,在三分x的基础上再三分y在y里面找一个满足条件的最小的一个,大题思路是这样,具体实现见代码。

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
int n; //n栋楼
double minx,miny,maxx,maxy;  //找到四个边界
double ansx,ansy,sum;  //最后的结果
double midy;  //便于最后输出
double eps=0.001;
struct mq
{
double x;
double y;
int peo;
};
mq node[1005];

double dis(double px,double py)  //算sum值
{
int i;
double sum=0;
for(i=0;i<n;i++)
sum+=sqrt((px-node[i].x)*(px-node[i].x)+(py-node[i].y)*(py-node[i].y))*node[i].peo;
return sum;
}

double cal(double x)  //每次返回x确定三分y使得dis(x,y)最小的x,y.
{
double lefty,righty,mimidy;
lefty=miny,righty=maxy;
while(righty-lefty>eps)
{
midy=(lefty+righty)/2.0,mimidy=(righty+midy)/2.0;
if(dis(x,midy)<dis(x,mimidy))
righty=mimidy;
else
lefty=midy;
}
return dis(x,midy);
}

int main()
{
int i;
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
while(~scanf("%d",&n))  //n栋楼
{
scanf("%lf%lf%d",&node[0].x,&node[0].y,&node[0].peo);
minx=maxx=node[0].x; miny=maxy=node[0].y;
for(i=1;i<n;i++)   //找到四个边界
{
scanf("%lf%lf%d",&node[i].x,&node[i].y,&node[i].peo);
if(node[i].x<minx) minx=node[i].x;
if(node[i].x>maxx) maxx=node[i].x;
if(node[i].y<miny) miny=node[i].y;
if(node[i].y>maxy) maxy=node[i].y;
}

double leftx,rightx,midx,mimidx;
leftx=minx,rightx=maxx;
while(rightx-leftx>eps)  //三分x的基础上三分y,找到最小的.感谢小雨,gcz
{
midx=(leftx+rightx)/2.0,mimidx=(rightx+midx)/2.0;
if(cal(midx)<cal(mimidx))
rightx=mimidx;
else
leftx=midx;
}
ansx=midx,ansy=midy;
sum=dis(ansx,ansy);
cout<<"便利店选址坐标为:"<<endl;
cout<<"x: "<<ansx<<"   "<<"y: "<<ansy<<endl;
cout<<"最优解为: "<<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.5389   y: 27.4345
最优解为: 5146.85
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: