数据结构——中国邮路问题

研究契机

本学期学校开设数据结构课程,研究了一下中国邮路问题,以供自己复习参考。

中国邮路问题

中国邮路问题是中国学者于20世纪50年代提出的一种典型的组合优化问题,后在国际上被称为中国邮路问题(Chinese postman problem)。已知图G=(V,E),对于每条边e∈E,有距离d(e),从G的某节点出发,走过G的所有边(允许重复穿过),回到原出发点,使其总行程最短,这个问题是P问题。
中国邮路问题内容:

邮递员的工作是每天在邮局里选出邮件,然后送到他所管辖的客户中,再返回邮局。自然地,若他要完成当天的投递任务,则他必须要走过他所投递邮件的每一条街道至少一次。找出怎样的走法使他的投递总行程为最短。

解题实践

算法的设计思想为:随机选取点添边,同时构建并查集检验图的连通性,若生成的图非连通则添边,若仍不满足条件,则重新构建直到满足条件。将符合要求的图写入文件。通过Floyd算法获得距离数组和路径数组,用于后续匹配和查找。获得奇点下标以及奇点个数,用状压dp进行最小权匹配,获得奇点对。在原图中添加边消除奇点获得欧拉图,用DFS找到一条欧拉回路。在欧拉回路中,完善不直接相连奇点的路径,获得实际路径。获取点的坐标,固定邮局A点,根据结点个数确定权值,判断两点间距离,如果太小则重新选取,最终得到分布均匀的图。

部分代码

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <graphics.h>
#include <iostream>
#include <string>

#define INF 65535          //定义无穷大
#define MaxVerNum  100     //定义最大顶点个数
typedef struct point elementType;  //定义图中顶点的数据类型
typedef double cellType;      //定义邻接矩阵中元素的数据类型

bool visited[MaxVerNum+1];  //全局数组,标记顶点是否已经访问,visited[0]单元不用
bool path[MaxVerNum+1][MaxVerNum+1];//全局数组,标记顶点之间是否有路径,path[0][] path[][0]不用
struct point
{
	double x;
	double y;
};  //存储点的信息 

typedef struct GraphAdjMatrix
{
	cellType AdjMatrix[MaxVerNum+1][MaxVerNum+1];  //邻接矩阵,数组下标为0单元不用,从AdjMatrix[1][1]单元开始
	elementType Pt[MaxVerNum+1];                 //顶点数组,存放各点的x,y坐标,pt[0]不用 
	int VerNum;       //顶点数
} Graph;  //图的类型名

double dst(struct point p,struct point q);
struct point mid(struct point p,struct point q);
void input(Graph &G);
void arrange(Graph &G);
void output(Graph &G);

//功能:用户交互输入计算机数目,x,y坐标,并存储到图中
void input(Graph &G)
{
	int i=0;
	int j=0;
	int n;
	printf("Input the number of the computer:\n");
	scanf("%d",&n);
	printf("input the x,y coordinate!\n");
	G.VerNum = n;
	//循环输入存储xy坐标 
	while(i<n)
	{
		double t1,t2;
		scanf("%lf%lf",&t1,&t2);
		G.Pt[i+1].x=t1;
		G.Pt[i+1].y=t2; 
		i++;
	}
	//将点的信息保存在图中 
	for (i=0;i<n;i++)
		for (j=0;j<n;j++)
		{
			if(i==j)
				G.AdjMatrix[i+1][j+1]=INF;//自己与自己的距离设置为无穷大
			else
				G.AdjMatrix[i+1][j+1]=dst(G.Pt[i+1],G.Pt[j+1]);	
		}
} 

//功能:计算使电缆长度最短的连接方案,存储在path[][]中
void arrange(Graph &G)
{
	int i,j,n;
	double min=INF;
	int p1=0;
	int p2=0;
	n=G.VerNum;
	//第一次循环 找出距离最短的两个点 
	for(i=0;i<n;i++)
		for(j=0;j<n;j++)
		{
			if(G.AdjMatrix[i+1][j+1]<min)
			{
				min=G.AdjMatrix[i+1][j+1];
				if(i==p2 && j==p1)//重复跳过,例如第一个点到第二个点和第二个点到第一个点是相同的
					continue;
				p1=i;
				p2=j;
			}
		}
	path[p1+1][p2+1]=true;
	path[p2+1][p1+1]=true;
	visited[p1+1]=true;
	visited[p2+1]=true;
	int k;
	//找出剩余n-2个点的连接方式	
	for(j=0;j<n-2;j++)
	{
		bool flag=false;
		min=INF;
		//找与p1距离最近的点 
		for(i=0;i<n;i++)
		{
			if(G.AdjMatrix[p1+1][i+1]<min && !path[p1+1][i+1] && !visited[i+1])
			{
				min=G.AdjMatrix[p1+1][i+1];
				k=i;
			}			
		}
	    //找与p2距离最近的点		
		for(i=0;i<n;i++)
		{
			if(G.AdjMatrix[p2+1][i+1]<min && !path[p2+1][i+1] && !visited[i+1])
			{
				min=G.AdjMatrix[p2+1][i+1];
				k=i;
				flag=true;//表示距离p2比p1近 
			}			
		}
		visited[k+1]=true;	
		if(flag)
		{
			path[p2+1][k+1]=true;
			path[k+1][p2+1]=true;
			p2=k;
		}
		else
		{
			path[p1+1][k+1]=true;
			path[k+1][p1+1]=true;
			p1=k;
		}
	}
}

//功能:输出输出使电缆长度最短的连接方案,以图形化界面显示
void output(Graph &G)
{
	int n=G.VerNum;
	initgraph(640,480);//初始化窗体 
	double sum=0;
	char str[20] = "" ;
	struct point m;
	setfillcolor(RED);// 设置填充颜色 
	setcolor(WHITE);//设置颜色 
	for(int i=0;i<n;i++)
	{	
		sprintf(str,"(%.f,%.f)",G.Pt[i+1].x,G.Pt[i+1].y);
		fillcircle(G.Pt[i+1].x*20,400-G.Pt[i+1].y*20,3);
		outtextxy(G.Pt[i+1].x*20,400-G.Pt[i+1].y*20,str);//输出点的文本信息 
		for(int j=0;j<n;j++)
		{
			if(path[i+1][j+1])
			{
				
				line(G.Pt[i+1].x*20,400-G.Pt[i+1].y*20,G.Pt[j+1].x*20,400-G.Pt[j+1].y*20);//绘制线 
				m = mid(G.Pt[i+1],G.Pt[j+1]);
				sprintf(str,"%.2fft",G.AdjMatrix[i+1][j+1]);
				outtextxy(m.x*20,400-m.y*20,str);//输出线的文本信息 
				path[j+1][i+1]=false;
				sum+=G.AdjMatrix[i+1][j+1]+16;
				printf("+(%.2f + 16.00)",G.AdjMatrix[i+1][j+1]);
			}
		}
	}
	sprintf(str,"sum=%.2lf",sum);
	outtextxy(320,10,str);//输出总长 
	printf("=%.2lf\n",sum);
	system("pause");
}

//功能:计算并返回两点距离
double dst(struct point p,struct point q)
{
	double d=(p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y);
	return sqrt(d);
}
//功能:计算并返回两点中点
struct point mid(struct point p,struct point q)
{
	struct point m;
	m.x = (p.x + q.x)/2.;
	m.y = (p.y + q.y)/2.;
	return m;
}

调试过程

在算法实现规程中,对于递归形式的算法,要注意考虑递归的层次,确保结果正确。在最开始研究的过程中由于我没有弄清最小权匹配算法的定义,陷入了困难,最后发现有边连接的奇点也可以添边。关于保存路径,因为要完善最终路径,所以需要能够添加结点的数据结构,选择链表可以在一定程度上简化问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值