您的位置:首页 > 其它

邮局选址问题

2011-03-17 17:19 260 查看
邮局选址问题
★问题描述:
在一个按照东西和南北方向划分成规整街区的城市里,n个居民点散乱地分布在不同的
街区中。用x 坐标表示东西向,用y坐标表示南北向。各居民点的位置可以由坐标(x,y)表示。
街区中任意2 点(x1,y1)和(x2,y2)之间的距离可以用数值|x1-x2|+|y1-y2|度量。
居民们希望在城市中选择建立邮局的最佳位置,使n个居民点到邮局的距离总和最小。
★编程任务:
给定n 个居民点的位置,编程计算n 个居民点到邮局的距离总和的最小值。
★数据输入:
由文件input.txt 提供输入数据。文件的第1 行是居民点数n,1£n£10000。接下来n 行
是居民点的位置,每行2 个整数x 和y,-10000£x,y£10000。
★结果输出:
程序运行结束时,将计算结果输出到文件output.txt 中。文件的第1 行中的数是n 个居
民点到邮局的距离总和的最小值。
★输入文件示例 输出文件示例
Input.txt output.txt
5
1 2
2 2
1 3
3 -2
3 3 10
运行环境: Windows XP,(开发环境:VC6.0)
运行过程说明: 本实验设计并实现了三种算法,
双击postOfficeQuickSort.exe或postOfficeRandomSelect.exe或postOfficeTimeLinearSelect.exe
按照提示输入 数据文件:input_assign01_xx.dat,每次输入一个文件名,回车,即可在output.txt中查看输出结果。
算法设计(包括算法设计过程中相关内容的说明、数据结构的选择、 算法详细描述及算法分析):
●1.设计说明:
设给定的n个居民点的位置坐标为:(x0,y0),(x1,y1),...,(xn-1,yn-1)。
设x0,x1,...,xn-1的中位数为median(x) 。通过进一步观察可知, median(x) 是的最优解。
邮局选址问题实际上是求中位数的问题。
●2.算法设计:
2.1数据结构:
将文件中读入的居民点坐标存放于数组中,横纵坐标各保存在一个数组中。
2.2算法描述:

postOffice()
{
int dx = getMidX(); //求横坐标中位数
int dy = getMidY(); //求纵坐标中位数
for(int i=0;i<n;i++) //计算距离总和
Distance += abs(x[i]-dx)+abs(y[i]-dy);
}
求中位数的过程用三种算法进行了设计:
快速排序选择中位数
快速排序算法qsort将n个居民点的x 坐标和y坐标分别排序后,计算出中位数median(x)和median( y),由此容易求得n个居民点到邮局的距离总和的最小值。
//--------------------------------------------
//求居民点到邮局的距离和的最小值
//prameter:横坐标数组,纵坐标数组,数组大小
//return:居民点到邮局的距离和的最小值
//--------------------------------------------
int postOffice(int * x, int * y, int size)
{
qsort(x,size,sizeof(int),compare); //快排数组x
qsort(y,size,sizeof(int),compare);
int min = getMinSumDistance(x,size) + getMinSumDistance(y,size);
//居民点到邮局的距离和的最小值
return min;
}
(2)随机选择法求中位数
用随机选择算法randomizedSelect 选择一个基准数,以此进行二分划分,计算出中位数median(x) 和median( y),然后计算n个居民点到邮局的距离总和的最小值。
//--------------------------------------------
//求居民点到邮局的距离和的最小值
//prameter:横坐标数组,纵坐标数组,数组大小
//return:居民点到邮局的距离和的最小值
//--------------------------------------------
int postOffice(int * x, int * y, int size)
{
int minX = randomizedSelect(x, 0, size - 1,(size + 1)/2);
int minY = randomizedSelect(y, 0, size - 1,(size + 1)/2);
int min = getMinSumDistance(x, size, minX) + getMinSumDistance(y, size, minY);
return min;
}
(3)线性时间选择求中位数
用最坏情况下的线性时间选择算法select计算出中位数median(x)和
median( y),然后计算n个居民点到邮局的距离总和的最小值。
//--------------------------------------------
//求居民点到邮局的距离和的最小值
//prameter:横坐标数组,纵坐标数组,数组大小
//return:居民点到邮局的距离和的最小值
//--------------------------------------------
int postOffice(int * x, int * y, int size)
{
int minX = select(x, 0, size - 1,(size + 1)/2);
int minY = select(y, 0, size - 1,(size + 1)/2);
int min = getMinSumDistance(x, size, minX) + getMinSumDistance(y, size, minY);
return min;
}
最坏情况下的线性时间选择算法select:
(1)将n个输入元素划分成n/5的上界 个组,每组5个元素,只可能有一个组不是5个元素。用冒泡排序算法,将每组中的元素排好序,并取出每组的中位数,共n/5的上界 个。
 (2)找出这n/5的上界个元素的中位数。如果n/5的上界是偶数,就找它的2个中位数中较大的一个,以这个元素作为划分基准。
2.3算法分析
快速排序选择中位数
快排时间复杂度:O(nlogn),求距离和:O(n)
总复杂度:O(nlogn)。
(2)随机选择法求中位数
最坏情况下,randomizedSelect的时间复杂度是:O(n2),平均时间为O(n)
故时间复杂度为:O(n2)。
线性时间选择求中位数
最坏情况下,select算法时间复杂度为O(n),求距离和:O(n)
总复杂度:O(n)。
以上三种算法的空间复杂度均为O(n)。
其他说明:
本算法 设计说明部分 部分内容(公式)来自互联网

//=============================================
//postOfficeQuickSort.cpp
//邮局选址问题,由input_assign01_XX.dat输入数据,将结果打印并输出至output.txt
//快速排序选择中位数
//by leo
//3.17.2011
//=============================================
#include<iostream>
#include<algorithm>
#include<fstream>
#include<string>
using namespace std;
//---------------------------------------------
const int MAX_SIZE = 10000;   //居民点最大数目
int x[MAX_SIZE],y[MAX_SIZE];  //居民点横纵坐标
int numResident;			  //居民点数目
const string outFileName = "output.txt";
//输出文件名称
//---------------------------------------------
//根据用户输入,读取相应文件内容并将之保存到数组中
//---------------------------------------------
void input()
{
string str;
cout << "Please input the input file name(input_assign01_00.dat~input_assign01_10.dat):/n";
cin >> str;
ifstream fin(str.c_str());
//	ifstream fin("post9.txt");
fin >> numResident;
//将输入文件流中的数据读入数组中
for(int i = 0; i < numResident, fin >> x[i] >> y[i]; i++);
}
//---------------------------------------------
//将minDistance打印并输出至输出文件中
//---------------------------------------------
void output(int minDistance)
{
ofstream fout(outFileName.c_str());
fout << minDistance <<endl;//输出至文件output.txt
cout << minDistance <<endl;
}
//---------------------------------------------
//快速排序比较函数
//---------------------------------------------
int compare(const void * elem1,const void * elem2)
{
return *((int *)elem1) - *((int *)elem2);
}
//--------------------------------------------
//计算居民点横(纵)坐标到邮局横(纵)坐标的距离总和的最小值
//prameter:居民点横(纵)坐标数组,数组大小
//return:数组各元素与中间点差值的绝对值的和
//--------------------------------------------
int getMinSumDistance(int coordinate[], int size)
{
int sum = 0,mid;
mid = coordinate[size / 2];
for(int i = 0; i < size; i++)
sum += abs(coordinate[i] - mid);
return sum;
}
//--------------------------------------------
//求居民点到邮局的距离和的最小值
//prameter:横坐标数组,纵坐标数组,数组大小
//return:居民点到邮局的距离和的最小值
//--------------------------------------------
int postOffice(int * x, int * y, int size)
{
qsort(x,size,sizeof(int),compare); //快排数组x
qsort(y,size,sizeof(int),compare);
int min = getMinSumDistance(x,size) + getMinSumDistance(y,size);
//居民点到邮局的距离和的最小值
return min;
}
//--------------------------------------------
int main()
{
input();
output( postOffice(x,y,numResident) );
return 0;
}
//=============================================


//=============================================
//postOfficeRandomSelect.cpp
//邮局选址问题,由input_assign01_XX.dat输入数据,将结果打印并输出至output.txt
//随机选择法确定中位数
//by leo
//3.18.2011
//=============================================
#include<iostream>
#include<algorithm>
#include<fstream>
#include<string>
#include<time.h>
using namespace std;
//---------------------------------------------
const int MAX_SIZE = 10000;   //居民点最大数目
int x[MAX_SIZE],y[MAX_SIZE];  //居民点横纵坐标
int numResident;			  //居民点数目
const string outFileName = "output.txt";
//输出文件名称
//---------------------------------------------
//根据用户输入,读取相应文件内容并将之保存到数组中
//---------------------------------------------
void input()
{
string str;
cout << "Please input the input file name(input_assign01_00.dat~input_assign01_10.dat):/n";
cin >> str;
ifstream fin(str.c_str());
//	ifstream fin("post9.txt");
fin >> numResident;
//将输入文件流中的数据读入数组中
for(int i = 0; i < numResident, fin >> x[i] >> y[i]; i++);
}
//---------------------------------------------
//将minDistance打印并输出至输出文件中
//---------------------------------------------
void output(int minDistance)
{
ofstream fout(outFileName.c_str());
fout << minDistance <<endl;//输出至文件output.txt
cout << minDistance <<endl;
}
//--------------------------------------------
//产生start与end之间的随机数
//--------------------------------------------
int getRandom(int start, int end)
{
srand((unsigned)time(NULL));         //产生随机种子
int randSerial = start + (int)(rand()%100/100.0 * (end - start));
return randSerial;
}
//--------------------------------------------
//以数组中的一个随机数为基准,将coordinate数组二分,前小后大
//return:基准数的下标
//--------------------------------------------
int randomizedPartion(int coordinate[], int start, int end)
{
int randSerial = getRandom(start, end); //获取随机数
int pivot = coordinate[randSerial];     //产生基准数
int i = start - 1;
int j = end + 1;
while(1)                                //以基准数为基准将数组二分
{
while (coordinate[++i] < pivot && i <= end);
while (coordinate[--j] > pivot);
if (i >= j) break;
swap(coordinate[i], coordinate[j]);
}
return j;                               //返回基准数的下标
}
//--------------------------------------------
//求数组中第serial小的元素
//parameter:数组,起始下标,终止下标,待求第serial小的元素下标
//return:数组第serial小的元素
//--------------------------------------------
int randomizedSelect(int coordinate[],int start, int end, int serial)
{
if(start == end)
return coordinate[start + serial - 1];
int bound = randomizedPartion(coordinate,start,end);
//将coordinate数组二分,前小后大,返回随机数的下标
int numFront = bound - start + 1;  //计算以bound分界的前半区的个数
if(serial <= numFront)
return randomizedSelect(coordinate,start,bound,serial);
else
return randomizedSelect(coordinate,bound + 1,end,serial - numFront);
}
//--------------------------------------------
//计算数组coordinate中各数到mid的距离和
//return:距离和
//--------------------------------------------
int getMinSumDistance(int coordinate[], int size, int mid)
{
int sum = 0;
for(int i = 0; i < size; i++)
sum += abs(coordinate[i] - mid);
return sum;
}
//--------------------------------------------
//求居民点到邮局的距离和的最小值
//prameter:横坐标数组,纵坐标数组,数组大小
//return:居民点到邮局的距离和的最小值
//--------------------------------------------
int postOffice(int * x, int * y, int size)
{
int minX = randomizedSelect(x, 0, size - 1,(size + 1)/2);
int minY = randomizedSelect(y, 0, size - 1,(size + 1)/2);
int min = getMinSumDistance(x, size, minX) + getMinSumDistance(y, size, minY);
return min;
}
//--------------------------------------------
int main()
{
input();
output( postOffice(x,y,numResident) );
return 0;
}
//=============================================


//=============================================
//postOfficeTimeLinearSelect.cpp
//邮局选址问题,由input_assign01_XX.dat输入数据,将结果打印并输出至output.txt
//线性时间选择确定中位数
//by leo
//3.17.2011
//=============================================
#include<iostream>
#include<algorithm>
#include<fstream>
#include<string>
using namespace std;
//---------------------------------------------
const int MAX_SIZE = 10000;   //居民点最大数目
int x[MAX_SIZE],y[MAX_SIZE];  //居民点横纵坐标
int numResident;			  //居民点数目
const string outFileName = "output.txt";
//输出文件名称
//---------------------------------------------
//根据用户输入,读取相应文件内容并将之保存到数组中
//---------------------------------------------
void input()
{
string str;
cout << "Please input the input file name(input_assign01_00.dat~input_assign01_10.dat):/n";
cin >> str;
ifstream fin(str.c_str());
//	ifstream fin("post9.txt");
fin >> numResident;
//将输入文件流中的数据读入数组中
for(int i = 0; i < numResident, fin >> x[i] >> y[i]; i++);
}
//---------------------------------------------
//将minDistance打印并输出至输出文件中
//---------------------------------------------
void output(int minDistance)
{
ofstream fout(outFileName.c_str());
fout << minDistance <<endl;//输出至文件output.txt
cout << minDistance <<endl;
}
//--------------------------------------------
//以值pivot为基准将数组arr中arr[left]到arr[right]为两部分
//return:基准的下标
//--------------------------------------------
int partition(int arr[], int left, int right, int pivot)
{
int i = left - 1;
int j = right + 1;
while (true)
{
while (arr[++i] < pivot && i <= right);
while (arr[--j] > pivot);
if (i >= j) break;
swap(arr[i], arr[j]);
}
return j;
}
//--------------------------------------------
//将对arr[left]至arr[right]之间的数进行冒泡排序
//return:排序后的数组
//--------------------------------------------
int bubbleSort(int * arr, int left, int right)
{
for (int i = left; i < left + right; i++)
{
for(int j = i + 1; j <= right; j++)
{
if (*(arr + i) > *(arr + j))
swap(*(arr + i),*(arr + j));//temp = *(arr + i),*(arr + i) = *(arr + j),
//*(arr + j) = temp;
}
}
return *arr;
}
//--------------------------------------------
//将数组arr分组排序,并将中位数交换到arr的前面
//--------------------------------------------
void groupSort(int * arr,int left, int right)
{
//(right-left+1)/5为五元组的个数,即n/5的下界
for (int i = 0; i< (right - left + 1)/5; i++)
{
int pLeft = left + 5 * i;
int pRight = pLeft + 4;
bubbleSort(arr,pLeft,pRight);        //五元小组内排序
swap(arr[left + i],arr[pLeft + 2]);  //将arr[left+5*i]至arr[left+5*i+4]的
//第3小元素与arr[left+i]交换位置;
}
}
//--------------------------------------------
//选择数组arr中arr[left]到arr[right]中第serial小的元素,线性时间选择算法
//return:数组arr中arr[left]到arr[right]中第serial小的元素值
//--------------------------------------------
int select(int * arr,int left, int right, int serial)
{
if (right - left < 5)
{
bubbleSort(arr, left, right);        //用冒泡排序算法对数组arr[left:right]排序
return arr[left + serial - 1];
}
groupSort(arr,left,right);               //将数组arr分组排序,并将中位数交换到arr的前面
int pivot = select(arr, left, left + (right - left + 1)/5 - 1, (right - left + 6)/10);
//找中位数的中位数,(right-left+1)/5为五元组的个数
//(right-left+6)/10为中位数的中位数为第几小
int bound = partition(arr, left, right, pivot);
//以pviot为基准二分arr
int numFront = bound - left + 1;         //计算以bound分界的前半区的个数
if (serial <= numFront)
return select(arr, left, bound, serial);
else
return select(arr, bound + 1, right, serial-numFront);
}
//--------------------------------------------
//计算数组coordinate中各数到mid的距离和
//return:距离和
//--------------------------------------------
int getMinSumDistance(int coordinate[], int size, int mid)
{
int sum = 0;
for(int i = 0; i < size; i++)
sum += abs(coordinate[i] - mid);
return sum;
}
//--------------------------------------------
//求居民点到邮局的距离和的最小值
//prameter:横坐标数组,纵坐标数组,数组大小
//return:居民点到邮局的距离和的最小值
//--------------------------------------------
int postOffice(int * x, int * y, int size)
{
int minX = select(x, 0, size - 1,(size + 1)/2);
int minY = select(y, 0, size - 1,(size + 1)/2);
int min = getMinSumDistance(x, size, minX) + getMinSumDistance(y, size, minY);
return min;
}
//--------------------------------------------
int main()
{
input();
output( postOffice(x,y,numResident) );
return 0;
}
//=============================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: