贪心算法寻找最短路径【阿里数学竞赛外卖问题】

贪心算法求得的最短路径未必是最短的,这是当时看到阿里数学竞赛时写的一段程序(正好对图算法结构有所了解)。

题目如下:

a. 附图中有一个无向图,其中圈内数字代表一个地点,边e上数字代表长度L e (双向相同)。一位外卖小哥在起点A,要去3个商家(B 1 , B 2 , B 3 )取餐,送到3个对应的地方(C 1 , C 2 ,C 3 ),即B 1 至C 1 ,B 2 至C 2 , B 3 至C 3 。小哥的电动助力车的箱子同时最多装下2份外卖。

请问: 小哥该怎么走最短路径?这个最短路径的长度是多少?这里,A是出发点,最后一餐
  (不限次序)送达地为终点。为了简化问题,假设商家已经备好了外卖,小哥取餐送餐不用
  等。又假设每份外卖重量大小一样。

// 2.a.cpp : 定义控制台应用程序的入口点。
//贪心算法  最优距离 + 最优图

#include "stdafx.h"
#include "iostream"
using namespace std;
#define inf 999999
const int M = 15;
int mark[16];

struct NODE {
	int NUM;	//地址位
	int result;	//访问标记位 1表示标记
};
//bool compare(NODE* b, int& countB, NODE* c, int& countC, int(*tu)[16], int start1, int start2) {//当遇到两条一样长路径时
//	//比较两条路径最近一条路径的长度
//	//1B0C -> 2B1C
//	//1B1C -> 2B
//	//2B0C -> 1B2C
//	//2B1C -> 1B1C
//	//2B2C -> 1B
//	//3B -> C
//	return 1;
//}
void dfs(int cur, int dst,int dest,int (*tu)[16], int& minP) {//
	/***operation***/
	if (minP < dst) return;//当前走过路径大于之前最短路径,没必要再走下去  
	if (cur == dest) {//走到终点
		if (minP > dst) minP = dst;
		return;
	}
	else {
		int i;
		for (i = 1; i <= M; i++) {
			if (tu[cur][i] != inf && tu[cur][i] != 0 && mark[i] == 0) {
				mark[i] = 1;
				dfs(i, dst + tu[cur][i],dest,tu,minP);
				mark[i] = 0;
			}
		}
	}
	
}
int Find(NODE* b,int num) {
	int i = 0;
	while (b[i].result != num) {
		i++;
	}
	return i;
}
int temp;
//长度相同时递归调用 判决哪一处最优
void minDest(NODE* b,int& countB, NODE* c,int& countC, int(*tu)[16],int& start) {//start 为起点
	int i, j;
	int m;
	//int temp;
	int tempNum = 0;
	if (countB == 0) {
		j = 0; temp = inf;
		for (i = 0; i < 3; i++) {
			dfs(start, 0, b[i].NUM, tu, m = inf);
			if (m < temp) {
				temp = m;
				b[i].result = 1;
				j++;
				if (j > 1) {
					b[tempNum].result = 0;
				}
				tempNum = i;
			}
		}
		countB++;
		start = b[tempNum].NUM;//返回下一次的起点
	}
	else if (countB == 1) {
		int bOwn = Find(b,1); //此B处被mark
		if (countC == 0) {
			j = 0; temp = inf;
			for (i = 0; i < 3 ; i++) {
				if (i == bOwn) {
					continue;
				}
				dfs(start, 0, b[i].NUM, tu, m = inf);
				if (m < temp) {
					temp = m;
					b[i].result = 1;
					j++;
					if (j > 1) {
						b[tempNum].result = 0;
					}
					tempNum = i;
				}
			}
			dfs(start, 0, c[bOwn].NUM, tu, m = inf);
			if (m < temp) {
				temp = m;
				b[tempNum].result = 0;
				c[bOwn].result = 1;
				countC++;
				start = c[bOwn].NUM;
			}
			else {
				countB++;
				start = b[tempNum].NUM;
			}
		}
		else {//1b-1c则是对应的一对,只能取b
			j = 0; temp = inf;
			for (i = 0; i < 3 ; i++) {
				if (i == bOwn) {
					continue;
				}
				dfs(start, 0, b[i].NUM, tu, m = inf);
				if (m < temp) {
					temp = m;
					b[i].result = 1;
					j++;
					if (j >= 2) {
						b[tempNum].result = 0;
					}
					tempNum = i;
				}
			}
			countB++;
			start = b[tempNum].NUM;
		}
	}
	else if (countB == 2) {
		int bOwn0 = Find(b, 0);//b 中0所在位置
		if (countC == 0) {//此时只能去C,且与b对应的C
			j = 0; temp = inf;
			for (i = 0; i < 3  ; i++) {
				if (i == bOwn0) {
					continue; //去除0
				}
				dfs(start, 0, c[i].NUM, tu, m = inf);
				if (m < temp) {
					temp = m;
					c[i].result = 1;
					j++;
					if (j >= 2) {
						c[tempNum].result = 0;
					}
					tempNum = i;
				}
			}
			//dfs(start,0,b[bOwn0].NUM,tu,m = inf);
			//if (m < temp) {
				//temp = m;
				//b[bOwn0].result = 1;
				//c[tempNum].result = 0;
				//countB++;
				//return b[bOwn0].NUM;
			//}
			//else {
				countC++;
				start = c[tempNum].NUM;
			//}
		}
		else if (countC == 1) {//2b+1c 可走1b 1c
			int cOwn;
			int cOwn1 = Find(c, 1);//c的位置标记
			dfs(start, 0, b[bOwn0].NUM, tu, m = inf);
			temp = m;
			b[bOwn0].result = 1;
			i = 0;
			while (i < 3) { 
				if (i != bOwn0 && i != cOwn1) {
					cOwn = i;
				}
				i++;
			}
			dfs(start, 0, c[cOwn].NUM, tu, m = inf);
			if (m < temp) {
				temp = m;
				b[bOwn0].result = 0;
				c[cOwn].result = 1;
				countC++;
				start = c[cOwn].NUM;
			}
			else {
				countB++;
				start = b[bOwn0].NUM;
			}
		}
		else {//countC==2 只能走B
			dfs(start, 0, b[bOwn0].NUM, tu, m = inf);
			b[bOwn0].result = 1;
			temp = m;
			countB++;
			start = b[bOwn0].NUM;
		}
	}
	else {//countB==3
		if (countC >= 3) {//countC=3和不等于3
			start = inf;
		}
		else {//3B
			if (countC == 2) {
				int cOwn = Find(c, 0);
				dfs(start, 0, c[cOwn].NUM, tu, m = inf);
				temp = m;
				c[cOwn].result = 1;
				countC++;
				start = c[cOwn].NUM;
			}
			if (countC == 1) {
				int cOwn = Find(c, 1);
				j = 0; temp = inf;
				for (i = 0; i < 3 ; i++) {
					if (i == cOwn) {
						continue;
					}
					dfs(start, 0, c[i].NUM, tu, m = inf);
					if (m < temp) {
						temp = m;
						c[i].result = 1;
						j++;
						if (j >= 2) {
							c[tempNum].result = 0;
						}
						tempNum = i;
					}
				}
				countC++;
				start = c[tempNum].NUM;
			}
			if (countC == 0) {
				j = 0; temp = inf;
				for (i = 0; i < 3 ; i++) {
					dfs(start, 0, c[i].NUM, tu, m = inf);
					if (m < temp) {
						temp = m;
						c[i].result = 1;
						j++;
						if (j >= 2) {
							c[tempNum].result = 0;
						}
						tempNum = i;
					}
				}
				countC++;
				start = c[tempNum].NUM;
			}
		}
	}
}

int main()
{
	int i, j;
	int countB = 0, countC = 0;//A,B计数
	int tempLen = 0;//临时计长
	int tempMin = inf;
	int n = 15;//节点个数
	int tu[16][16];
	NODE A[1] = {2,0};
	NODE B[3] = { {3,0},{7,0},{4,0} };
	NODE C[3] = { {12,0},{11,0},{13,0 } };
	for (i = 0; i < 16; i++)
		for (j = 0; j < 16; j++)
			tu[i][j] = inf;//memset只能用于char[]类型的初始化0或1...
	for (i = 0; i < 16; i++)
		mark[i] = 0;
	for (i = 1; i <= n; i++)
		tu[i][i] = 0;
	tu[1][2] = tu[2][1] = 1; tu[1][5] = tu[5][1] = 1; tu[2][6] = tu[6][2] = 2; tu[2][3] = tu[3][2] = 2;
	tu[3][8] = tu[8][3] = 2; tu[3][4] = tu[4][3] = 1; tu[4][15] = tu[15][4] = 3; tu[5][6] = tu[6][5] = 1;
	tu[6][7] = tu[7][6] = 1; tu[5][9] = tu[9][5] = 1; tu[7][10] = tu[10][7] = 1; tu[7][8] = tu[8][7] = 1;
	tu[8][11] = tu[11][8] = 1; tu[9][12] = tu[12][9] = 2; tu[9][10] = tu[10][9] = 3; tu[10][11] = tu[11][10] = 1;
	tu[10][13] = tu[13][10] = 2; tu[11][14] = tu[14][11] = 1; tu[12][13] = tu[13][12] = 2; tu[13][14] = tu[14][13] = 1;
	tu[14][15] = tu[15][14] = 1;
	//贪心算法=最短路径+最优图
	int start = 2; int m = 0; i = 0;//temp 是全局变量
	while (i < 6) {
		minDest(B, countB, C, countC, tu, start);
		cout <<"第"<<i+1<<"段位移:"<< temp << endl;
		m += temp;
		cout <<"累计长度:"<< m << endl;
		i++;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40801709/article/details/85722097
今日推荐