贪心算法求得的最短路径未必是最短的,这是当时看到阿里数学竞赛时写的一段程序(正好对图算法结构有所了解)。
题目如下:
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;
}